Giter Club home page Giter Club logo

fastify-cookie's Introduction

@fastify/cookie

CI NPM version js-standard-style

A plugin for Fastify that adds support for reading and setting cookies.

This plugin's cookie parsing works via Fastify's onRequest hook. Therefore, you should register it prior to any other onRequest hooks that will depend upon this plugin's actions.

It is also possible to import the low-level cookie parsing and serialization functions.

@fastify/cookie v2.x supports both Fastify@1 and Fastify@2. @fastify/cookie v3 only supports Fastify@2.

Installation

npm i @fastify/cookie

or

yarn add @fastify/cookie

Example

const fastify = require('fastify')()

fastify.register(require('@fastify/cookie'), {
  secret: "my-secret", // for cookies signature
  hook: 'onRequest', // set to false to disable cookie autoparsing or set autoparsing on any of the following hooks: 'onRequest', 'preParsing', 'preHandler', 'preValidation'. default: 'onRequest'
  parseOptions: {}  // options for parsing cookies
})

fastify.get('/', (req, reply) => {
  const aCookieValue = req.cookies.cookieName
  // `reply.unsignCookie()` is also available
  const bCookie = req.unsignCookie(req.cookies.cookieSigned);
  reply
    .setCookie('foo', 'foo', {
      domain: 'example.com',
      path: '/'
    })
    .cookie('baz', 'baz') // alias for setCookie
    .setCookie('bar', 'bar', {
      path: '/',
      signed: true
    })
    .send({ hello: 'world' })
})

TypeScript Example

import type { FastifyCookieOptions } from '@fastify/cookie'
import cookie from '@fastify/cookie'
import fastify from 'fastify'

const app = fastify()

app.register(cookie, {
  secret: "my-secret", // for cookies signature
  parseOptions: {}     // options for parsing cookies
} as FastifyCookieOptions)

Importing serialize and parse

const { serialize, parse } = require('@fastify/cookie')
const fastify = require('fastify')()

fastify.get('/', (req, reply) => {
  const cookie = serialize('lang', 'en', {
    maxAge: 60_000,
  })

  reply.header('Set-Cookie', cookie)

  reply.send('Language set!')
})

Options

  • secret (String | Array | Buffer | Object):

    • A String or Buffer can be passed to use as secret to sign the cookie using cookie-signature.
    • An Array can be passed if key rotation is desired. Read more about it in Rotating signing secret.
    • More sophisticated cookie signing mechanisms can be implemented by supplying an Object. Read more about it in Custom cookie signer.
  • hook: the Fastify Hook to register the parsing of cookie into. Default: onRequest.

  • algorithm: the algorithm to use to sign the cookies. Default: sha256.

  • parseOptions: An Object to modify the serialization of set cookies.

⚠️ Security Considerations ⚠️

It is recommended to use sha256 or stronger hashing algorithm as well as a secret that is at least 20 bytes long.

parseOptions

domain

Specifies the value for the Domain Set-Cookie attribute. By default, no domain is set, and most clients will consider the cookie to apply to only the current domain.

encode

Specifies a function that will be used to encode a cookie's value. Since value of a cookie has a limited character set (and must be a simple string), this function can be used to encode a value into a string suited for a cookie's value.

The default function is the global encodeURIComponent, which will encode a JavaScript string into UTF-8 byte sequences and then URL-encode any that fall outside of the cookie range.

expires

Specifies the Date object to be the value for the Expires Set-Cookie attribute. By default, no expiration is set, and most clients will consider this a "non-persistent cookie" and will delete it on a condition like exiting a web browser application.

Note: the cookie storage model specification states that if both expires and maxAge are set, then maxAge takes precedence, but it is possible not all clients by obey this, so if both are set, they should point to the same date and time.

httpOnly

Specifies the boolean value for the HttpOnly Set-Cookie attribute. When truthy, the HttpOnly attribute is set, otherwise it is not. By default, the HttpOnly attribute is not set.

Note: be careful when setting this to true, as compliant clients will not allow client-side JavaScript to see the cookie in document.cookie.

maxAge

Specifies the number (in seconds) to be the value for the Max-Age Set-Cookie attribute. The given number will be converted to an integer by rounding down. By default, no maximum age is set.

Note: the cookie storage model specification states that if both expires and maxAge are set, then maxAge takes precedence, but it is possible not all clients by obey this, so if both are set, they should point to the same date and time.

partitioned

Specifies the boolean value for the Partitioned Set-Cookie attribute. When truthy, the Partitioned attribute is set, otherwise it is not. By default, the Partitioned attribute is not set.

⚠️ Warning: This is an attribute that has not yet been fully standardized, and may change in the future without reflecting the semver versioning. This also means many clients may ignore the attribute until they understand it.

More information about can be found in the proposal.

priority

Specifies the string to be the value for the Priority Set-Cookie attribute.

  • 'low' will set the Priority attribute to Low.
  • 'medium' will set the Priority attribute to Medium, the default priority when not set.
  • 'high' will set the Priority attribute to High.

More information about the different priority levels can be found in the specification.

⚠️ Warning: This is an attribute that has not yet been fully standardized, and may change in the future without reflecting the semver versioning. This also means many clients may ignore the attribute until they understand it.

path

Specifies the value for the Path Set-Cookie attribute. By default, the path is considered the "default path".

sameSite

Specifies the boolean or string to be the value for the SameSite Set-Cookie attribute.

  • true will set the SameSite attribute to Strict for strict same site enforcement.
  • false will not set the SameSite attribute.
  • 'lax' will set the SameSite attribute to Lax for lax same site enforcement.
  • 'none' will set the SameSite attribute to None for an explicit cross-site cookie.
  • 'strict' will set the SameSite attribute to Strict for strict same site enforcement.

More information about the different enforcement levels can be found in the specification.

Note: This is an attribute that has not yet been fully standardized, and may change in the future. This also means many clients may ignore this attribute until they understand it.

secure

Specifies the boolean value for the Secure Set-Cookie attribute. When truthy, the Secure attribute is set, otherwise it is not. By default, the Secure attribute is not set.

Note: be careful when setting this to true, as compliant clients will not send the cookie back to the server in the future if the browser does not have an HTTPS connection.

API

Parsing

Cookies are parsed in the onRequest Fastify hook and attached to the request as an object named cookies. Thus, if a request contains the header Cookie: foo=foo then, within your handler, req.cookies.foo would equal 'foo'.

You can pass options to the cookie parse by setting an object named parseOptions in the plugin config object.

Sending

The method setCookie(name, value, options), and its alias cookie(name, value, options), are added to the reply object via the Fastify decorateReply API. Thus, in a request handler, reply.setCookie('foo', 'foo', {path: '/'}) will set a cookie named foo with a value of 'foo' on the cookie path /.

  • name: a string name for the cookie to be set
  • value: a string value for the cookie
  • options: an options object as described in the cookie serialize documentation
  • options.signed: the cookie should be signed
  • options.secure: if set to true it will set the Secure-flag. If it is set to "auto" Secure-flag is set when the connection is using tls.

Securing the cookie

Following are some of the precautions that should be taken to ensure the integrity of an application:

  • It's important to use options.httpOnly cookies to prevent attacks like XSS.
  • Use signed cookies (options.signed) to ensure they are not getting tampered with on client-side by an attacker.
  • Use __Host- Cookie Prefix to avoid Cookie Tossing attacks.
  • it's important to use HTTPS for your website/app to avoid a bunch of other potential security issues like MITM etc.

Clearing

The method clearCookie(name, options) is added to the reply object via the Fastify decorateReply API. Thus, in a request handler, reply.clearCookie('foo', {path: '/'}) will clear a cookie named foo on the cookie path /.

  • name: a string name for the cookie to be cleared
  • options: an options object as described in the cookie serialize documentation. Its optional to pass options object

Manual cookie parsing

The method parseCookie(cookieHeader) is added to the fastify instance via the Fastify decorate API. Thus, fastify.parseCookie('sessionId=aYb4uTIhdBXC') will parse the raw cookie header and return an object { "sessionId": "aYb4uTIhdBXC" }.

Rotating signing secret

Key rotation is when an encryption key is retired and replaced by generating a new cryptographic key. To implement rotation, supply an Array of keys to secret option.

Example:

fastify.register(require('@fastify/cookie'), {
  secret: [key1, key2]
})

The plugin will always use the first key (key1) to sign cookies. When parsing incoming cookies, it will iterate over the supplied array to see if any of the available keys are able to decode the given signed cookie. This ensures that any old signed cookies are still valid.

Note:

  • Key rotation is only achieved by redeploying the server again with the new secret array.
  • Iterating through all secrets is an expensive process, so the rotation list should contain as few keys as possible. Ideally, only the current key and the most recently retired key.
  • Although previously signed cookies are valid even after rotation, cookies should be updated with the new key as soon as possible. See the following example for how to accomplish this.

Example:

fastify.get('/', (req, reply) => {
  const result = reply.unsignCookie(req.cookies.myCookie)

  if (result.valid && result.renew) {
    // Setting the same cookie again, this time plugin will sign it with a new key
    reply.setCookie('myCookie', result.value, {
      domain: 'example.com', // same options as before
      path: '/',
      signed: true
    })
  }
})

Custom cookie signer

The secret option optionally accepts an object with sign and unsign functions. This allows for implementing a custom cookie signing mechanism. See the following example:

Example:

fastify.register(require('@fastify/cookie'), {
  secret: {
    sign: (value) => {
      // sign using custom logic
      return signedValue
    },
    unsign: (value) => {
      // unsign using custom logic
      return {
        valid: true, // the cookie has been unsigned successfully
        renew: false, // the cookie has been unsigned with an old secret
        value: 'unsignedValue'
      }
    }
  }
})

Manual cookie unsigning

The method unsignCookie(value) is added to the fastify instance, to the request and the reply object via the Fastify decorate, decorateRequest and decorateReply APIs, if a secret was provided as option. Using it on a signed cookie will call the the provided signer's (or the default signer if no custom implementation is provided) unsign method on the cookie.

Example:

fastify.register(require('@fastify/cookie'), { secret: 'my-secret' })

fastify.get('/', (req, rep) => {
  if (fastify.unsignCookie(req.cookie.foo).valid === false) {
    rep.send('cookie is invalid')
    return
  }

  rep.send('cookie is valid')
})

Other cases of manual signing

Sometimes the service under test should only accept requests with signed cookies, but it does not generate them itself.

Example:

test('Request requires signed cookie', async () => {
    const response = await app.inject({
        method: 'GET',
        url: '/',
        headers: {
          cookies : {
            'sid': app.signCookie(sidValue)
          }
        },
    });

    expect(response.statusCode).toBe(200);
});

Manual signing/unsigning with low level utilities

with Signer

const { Signer } = require('@fastify/cookie');

const signer = new Signer('secret');
const signedValue = signer.sign('test');
const {valid, renew, value } = signer.unsign(signedValue);

with sign/unsign utilities

const { fastifyCookie } = require('@fastify/cookie');

const signedValue = fastifyCookie.sign('test', 'secret');
const unsignedvalue = fastifyCookie.unsign(signedValue, 'secret');

License

MIT License

fastify-cookie's People

Contributors

budarin avatar cemremengu avatar climba03003 avatar darkgl0w avatar delvedor avatar dependabot[bot] avatar eomm avatar fdawgs avatar fralonra avatar github-actions[bot] avatar greenkeeper[bot] avatar gurgunday avatar hronro avatar jsumners avatar kibertoad avatar mcollina avatar mse99 avatar ryanagillie avatar salmanm avatar serayaeryn avatar simoneb avatar skellla avatar stephan9mertel avatar svjard avatar thomheymann avatar uzlopak avatar vatson avatar vitaly-sazonov avatar wight554 avatar zekth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fastify-cookie's Issues

`unsignCookie` function present in the fastify instance object, but not in the TypeScript types file

🐛 Bug Report

Not a bug per se, but the plugin.d.ts file is missing the unsignCookie type signature for the FastifyInstance type

To Reproduce

Call the unsignCookie function on a FastifyInstance object in TS, it won't type check.

import fastify from "fastify"
import fastifyCookie from "fastify-cookie"

// ...

const app = fastify()

await app.register(fastifyCookie, {
  // ...,
})

// ...

app.unsignCookie(aSignedCookie)

Leads to the error "The property unsignCookie doesn't exist on type FastifyInstance<...>"

Expected behavior

Should type check.

Your Environment

  • node version: 15
  • fastify version: >=3.15.0
  • os: Mac

How do I remove a cookie?

Hi,

Could you give an example please how to remove a cookie using fastify reply? Unfortunately, cannot find any methods for this action, while it is quite needed, especially for "httpOnly" cookies.

Thank you in advance!

Unsign return signature breaking change in 4.x release line

💥 Regression Report

Prior to the 4.2.0 release, reply.unsignCookie(...) would return a string. After the PR, it now returns an object. It looks like previously you were using cookie-signature's logic directly, but with the custom signer, you started wrapping and returning your own object.

Breaking commit: v4.2.0...master#diff-da0818d1007a8f324a63ef085c6b9bbd765b262a841385b2e7b0f28f230ce349R29

Last working version

Worked up to version: 4.1.1

Stopped working in version: 4.2.0

To Reproduce

Try unsigning a cookie.

reply.unsignCookie(req.cookies.mycoolcookie);
// returns string in 4.1.1
// returns object in 4.2.0

Expected behavior

It's unclear whether this was an intentional breaking change or if there was a misunderstanding of the existing signature. Ultimately, with 4.2.0 you need to grab the value property of the unsigned cookie object to restore similar/old behavior, but I wanted to confirm if this was correct/expected behavior.

I created this issue more so to clarify, as I was confused when bumping versions and had things start breaking.

Your Environment

  • node version: 14.15.4
  • fastify version: >=3.11.0
  • os: Linux

Allow passing options to cookie parser

🚀 Feature Proposal

cookie.parse has a second argument options. Though it only support a decode property that defines a decoder function right now, it can be useful in some cases.

I think we could add a plugin-level option, which would be passed to cookie.parse.

BTW, I think we could use a if statement here, because fastifyReq.cookies is already set to an empty object in L48.

fastifyReq.cookies = (cookieHeader) ? cookie.parse(cookieHeader) : {}

Example

fastify.register(require('fastify-cookie'), {
  secret: "my-secret",
  parseOptions: {
    decode: decoder
  }
})

Property 'setCookie' does not exist on type 'FastifyReply<ServerResponse>'.

Installed fastify-cookie with npm install fastify-cookie.
Using VSCode.
fastify v.2.0.1
fastify-cookie v.3.0.2
error:

{
	"code": "2339",
	"message": "Property 'setCookie' does not exist on type 'FastifyReply<ServerResponse>'.",
	"source": "ts",
}

Code:

const fastifyCookie = require("fastify-cookie");
export const renderMiddleware = (fastify: FastifyInstance) => {
    fastify.register(fastifyCookie);
    fastify.post("/create-session-cookie", async (req, res) => {
       
        try {
            const sessionCookie = await thriftClient.createSession(
                "blabla",
                tokenExpDate,
                lastIp,
                tokenData,
                null
            );

            res.setCookie("sessionId", sessionCookie.sessionId, {}) < Throws error
                .status(200)
                .send(sessionCookie);
        } catch (e) {
            req.log.info(JSON.stringify(e));
            res.status(500).send(e);
            return;
        }
    });
}

Deprecation warning with Fastify 3.9.0

🐛 Bug Report

Upgraded Fastify from 3.8.0 to 3.9.0, and the following warning shows up:

[FSTDEP006] FastifyDeprecation: You are decorating Request/Reply with a reference type. This reference is shared amongst all requests. Use onRequest hook instead. Property: cookies

I have tracked the issue to plugin.js#L56

To Reproduce

Use fastify-cookie per documented example

Expected behavior

No warnings

Your Environment

  • node version: v14.15.0
  • fastify version: 3.9.0
  • os: Mac

If no cookie header is set, cookies are preserved from previous request

🐛 Bug Report

In case my user clears all of his cookies, this condition will not execute:

    const cookieHeader = fastifyReq.req.headers.cookie
    if (cookieHeader) {
      fastifyReq.cookies = cookie.parse(cookieHeader, options)
    }

and fastifyReq.cookies is gonna remain the same as it was in the previous request.

To Reproduce

Set a cookie and make a request, then clear all of your browser cookies and examine req.cookies on the next request.

Expected behavior

Cookies decorator should reset on each request.

    const cookieHeader = fastifyReq.req.headers.cookie
    fastifyReq.cookies = cookieHeader ? cookie.parse(cookieHeader, options) : {}

Integrate cookie dependency and implement faster parsing algorithm

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the feature has not already been requested

🚀 Feature Proposal

The current dependency "cookie" is widely used. There are some PRs open, which aim to improve the performance, but they arent merged, despite being there for 3-4 years.

I propose we integrate the code from "cookie" and improve the performance in our codebase.

benchmark with current implementation:

cookie.parse

6 tests completed.

simple      x 1,886,402 ops/sec ±3.40% (171 runs sampled)
decode      x 2,333,643 ops/sec ±0.18% (194 runs sampled)
unquote     x 2,505,649 ops/sec ±0.26% (189 runs sampled)
duplicates  x   997,104 ops/sec ±0.16% (194 runs sampled)
10 cookies  x   303,594 ops/sec ±0.13% (192 runs sampled)
100 cookies x    28,964 ops/sec ±0.20% (193 runs sampled)

optimized parse:

function parse(str, options) {
  if (typeof str !== 'string') {
    throw new TypeError('argument str must be a string');
  }

  var result = {}
  var dec = options && options.decode || decode;

  var pos = 0;
  var terminatorPos = 0;
  var eq_idx = 0;

  while (true) {
    if (terminatorPos === str.length) {
      break;
    }
    terminatorPos = str.indexOf(";", pos);
    terminatorPos = (terminatorPos === -1) ? str.length : terminatorPos;
    eq_idx = str.indexOf('=', pos);

    // skip things that don't look like key=value
    if (eq_idx === -1 || eq_idx > terminatorPos) {
      pos = terminatorPos + 1;
      continue;
    }

    var key = str.substring(pos, eq_idx++).trim()
    
    // only assign once
    if (undefined == result[key]) {
      var val = (str.charCodeAt(eq_idx) === 0x22)
        ? str.substring(eq_idx + 1, terminatorPos - 1).trim()
        : str.substring(eq_idx, terminatorPos).trim();
  
      result[key] = (dec !== decode || -1 !== val.indexOf('%'))
        ? tryDecode(val, dec)
        : val;
    }
    pos = terminatorPos + 1;
  }
  return result;
}

benchmark with optimized version

cookie.parse

6 tests completed.

simple      x 4,148,129 ops/sec ±1.96% (172 runs sampled)
decode      x 2,879,839 ops/sec ±0.19% (193 runs sampled)
unquote     x 6,790,136 ops/sec ±0.50% (190 runs sampled)
duplicates  x 1,922,513 ops/sec ±0.42% (192 runs sampled)
10 cookies  x   598,713 ops/sec ±0.25% (192 runs sampled)
100 cookies x    47,375 ops/sec ±0.27% (189 runs sampled)

We could gain about 100% better performance.

Motivation

More Power.

Example

No response

Adds ability to unsign cookies via request

🚀 Feature Proposal

Adds ability to unsign cookies via request.

Motivation

  • In this case, the request becomes self-sufficient and there is no need to pass reply as an argument to other methods.
  • NestJS allows to inject only request object to the constructor of Request scoped services. This is logical because services should not compose a reply. It should be done in other layers (controllers, interceptors)

Example

fastify.get('/', (req, reply) => {
  const mySecretCookie = req.unsignCookie(req.cookies.mySecretCookie);
  reply
    .setCookie('mySecretCookie', 'value', {
      path: '/',
      signed: true
    })
    .send({ hello: 'world' })
})

`clearCookie` does not use parseOptions

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

4.2.1

Plugin version

8.1.0

Node.js version

16.17.0

Operating system

Linux

Operating system version (i.e. 20.04, 11.3, 10)

20.04

Description

I'm not sure if it is a bug or a feature request -> I decided to use the bug template because I spend some time for debugging it.

From my point of view, the clearCookie method should respect parseOptions passed during the plugin initialization, similarly to setCookie. It is especially useful to remove cookies on a specific domain.

Steps to Reproduce

  const fastify = Fastify()
  fastify.register(plugin, {
    parseOptions: {
      path: '/test',
      domain: 'example.com'
    }
  })

  fastify.get('/test1', (req, reply) => {
    reply
      .clearCookie('foo')
      .send({ hello: 'world' })
  })

The cleared cookie has not set a path and a domain.

{
  name: 'foo',
  value: '',
  expires: 1970-01-01T00:00:00.000Z
}

Expected Behavior

The cleared cookie includes options obtained from parseOptions e.g. includes the path and the domain.

{
  name: 'foo',
  value: '',
  domain: 'example.com',
  path: '/test',
  expires: 1970-01-01T00:00:00.000Z
}

New activity in original cookie package, evaluate it

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the issue has not already been raised

Issue

It seems that the cookie package got updates. It should be checked if it is faster than our modified code, and if we can supply improvements and maybe switch back to the original cookie package in fastify cookie.

@dougwilson
Are you accepting PRs or did you move on again and I missed a "project focus"-window?

Request Cookies Should Be Typed As `string | undefined`

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

3.29.0

Plugin version

6.0.0

Node.js version

16.15.1

Operating system

Windows

Operating system version (i.e. 20.04, 11.3, 10)

Windows 11 Pro Build 22000.739

Description

Right now when using the package any cookie is always typed as a string when it could be undefined.

Pretty simple fix, just changing the type from cookies: { [cookieName: string]: string }; to cookies: { [cookieName: string]: string | undefined };

(if you wanted to make it more TypeScript-y you could do cookies: Record<string, string | undefined>;

Steps to Reproduce

fastify.get('/', async (reqeust, reply) => {
  const { someCookieThatDoesNotExist } = request.cookies; // someCookieThatDoesNotExist: string
  console.log(someCookieThatDoesNotExist); // undefined
  console.log(typeof someCookieThatDoesNotExist); // undefined
  
  return { hello: 'world' }
});

Expected Behavior

No response

Broken typings

node_modules/fastify-cookie/plugin.d.ts:12:19 - error TS2744: Type parameter defaults can only reference previously declared type parameters.

12     HttpRequest = HttpRequest,
                     ~~~~~~~~~~~

Exported types doesn't includes parseCookie method for fastify instance

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

3.22.1

Plugin version

5.3.1

Node.js version

16.11.1

Operating system

Windows

Operating system version (i.e. 20.04, 11.3, 10)

10

Description

The parseCookie is not include in the .d.ts file.
This causes that when using the method through the fastify instance, a typing error is generated even though the method does exist in the instances.

Steps to Reproduce

import fastifyCookie from 'fastify-cookie'
import fp from 'fastify-plugin'
import fastifySession from 'fastify-session'

export default fp<any>(async (fastify, opts) => {
  fastify.register(fastifyCookie)
  fastify.register(fastifySession, {
    secret: String(process.env.APP_KEY),
    saveUninitialized: false,
    cookie: {
      httpOnly: true,
      secure: process.env.NODE_ENV !== 'production'
    }
  })

  // TS Error: Property 'parseCookie' does not exist on type 'FastifyInstance<Server, IncomingMessage, ServerResponse, FastifyLoggerInstance>'.ts(2339)
  fastify.parseCookie('cookie')
})

Expected Behavior

I did a little fix, creating the typing in the creation of a plugin, for example.
But I think it is something that should be included in the .dt.s

import fastifyCookie from 'fastify-cookie'
import fp from 'fastify-plugin'
import fastifySession from 'fastify-session'

export default fp<any>(async (fastify, opts) => {
  fastify.register(fastifyCookie)
  fastify.register(fastifySession, {
    secret: String(process.env.APP_KEY),
    saveUninitialized: false,
    cookie: {
      httpOnly: true,
      secure: process.env.NODE_ENV !== 'production'
    }
  })

  fastify.parseCookie('cookie')
})

// here is my simple fix
declare module 'fastify' {
  export interface FastifyInstance {
    parseCookie(cookieHeader: string): Record<string, any>
  }
}

Most basic functionality not working

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure it has not already been reported

Fastify version

3.18.1

Plugin version

5.3.1

Node.js version

12

Operating system

Linux

Operating system version (i.e. 20.04, 11.3, 10)

20.10

Description

The most basic functionality as shown in the intro docs is not working even after spending some 2 hours trying to figure things out. Please make documentation more elaborate if possible.

Steps to Reproduce

No specific case. The steps in the docs itself don't work. The cookie is not set.

index.js

fastify.register(require('fastify-cookie'), {
   secret: 'xyz',
   parseOptions: {}
})

in my route handler

reply
   .code(200)
   .cookie('foo', 'bar')
   .send({message: 'Hello world'})

Expected Behavior

The desired cookie should be set.

Payload typing lost when using setCookie before

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

3.27.1

Plugin version

5.5.0

Node.js version

16.13.1

Operating system

Windows

Operating system version (i.e. 20.04, 11.3, 10)

11 (21H2)

Description

When using a schema for my route, I get payload typing. But when I use setCookie on my reply, the payload get the type unknown.

image
image

Steps to Reproduce

type TSchemaResponse = {
    message: string
};

// ...
fastifyInstance.post<Response: TSchemaResponse>(
    "/hello",
    (request, reply) => {
        reply.send({message: 123}); // typescript error as message should be a string
    }
);
// ...
type TSchemaResponse = {
    message: string
};

// ...
fastifyInstance.post<Response: TSchemaResponse>(
    "/hello",
    (request, reply) => {
        reply
            .setCookie("hello", "there")
            .send({message: 123}); // no error (there should be an error)
    }
);
// ...

Expected Behavior

The payload should still be typed after calling setCookie (as setCookie should return "this" type)

Allow passing signed:true to clearCookie

🐛 Bug Report

I tried setting and unsetting a cookie by storing all the options in an object:

const SESSION_COOKIE_OPTIONS: CookieSerializeOptions = {
    sameSite: 'lax',
    signed: true,
    secure: false,
    maxAge: 24*60*60*30,
    httpOnly: true,
    path: '/',
}

This works fine for setting:

reply.setCookie(SESSION_COOKIE_NAME, sessionKeyStr, SESSION_COOKIE_OPTIONS)

But throws an error when unsetting:

reply.clearCookie(SESSION_COOKIE_NAME, SESSION_COOKIE_OPTIONS)

TypeError: Secret string must be provided.

If you just rip the signed option out in the fastifyCookieClearCookie, then it should work fine.

You should remove maxAge too.

To Reproduce

Above.

Expected behavior

No error.

Your Environment

❯ yarn list --depth=0 | grep fastify
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]
├─ [email protected]

Strange error object

I started to see error in console from fastify-cookies.
Everything seems works fine.
I don't know where to look problem because error looks strange.
Maybe now it should be used without callback with error:
fastify.register(require('fastify-cookie'))

Last versions of everything.

const fastify = require('fastify')({ logger: true })

fastify.register(require('fastify-cookie'), err => {
    if (err) console.log(err)
})

// Declare a route
fastify.get('/', async (request, reply) => {
  return { hello: 'world' }
})

// Run the server!
const start = async () => {
  try {
    await fastify.listen(3000)
    fastify.log.info(`server listening on ${fastify.server.address().port}`)
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

Error object:

{ getSchemas: [Function: bound ],
  [Symbol(fastify.children)]:
   [ { getSchemas: [Function: bound ],
       [Symbol(fastify.children)]: [Array],
       [Symbol(fastify.Reply)]: [Function: _Reply],
       [Symbol(fastify.Request)]: [Function: _Request],
       [Symbol(fastify.contentTypeParser)]: [ContentTypeParser],
       [Symbol(fastify.hooks)]: [Hooks],
       [Symbol(fastify.routePrefix)]: '',
       [Symbol(fastify.logLevel)]: '',
       [Symbol(fastify.middlewares)]: [],
       [Symbol(fastify.schemas)]: [Schemas],
       [Symbol(registered-plugin)]: Array {} } ],
  [Symbol(fastify.Reply)]: [Function: _Reply],
  [Symbol(fastify.Request)]: [Function: _Request],
  [Symbol(fastify.contentTypeParser)]:
   ContentTypeParser {
     customParsers: { 'application/json': [Parser], 'text/plain': [Parser] },
     parserList: [ 'application/json', 'text/plain' ],
     cache:
      LRU { first: null, items: {}, last: null, max: 100, size: 0, ttl: 0 },
     [Symbol(fastify.defaultJSONParse)]: [Function: defaultJsonParser] },
  [Symbol(fastify.hooks)]:
   Hooks {
     onRequest: [ [Function: bound onRateLimit] ],
     preParsing: [],
     preValidation: [],
     preSerialization: [],
     preHandler: [],
     onResponse: [],
     onSend: [],
     onError: [] },
  [Symbol(fastify.routePrefix)]: '',
  [Symbol(fastify.logLevel)]: '',
  [Symbol(fastify.middlewares)]: [],
  [Symbol(fastify.schemas)]: Schemas { store: {} },
  [Symbol(registered-plugin)]: Array { '1': 'fastify-cookie', length: 2 } }

Can't access cookie in preHandler

The cookie object doesn't seem to exist in the preHandler hook inside my routes, see my code below;

// routes/auth.js
// inside routes function
  fastify.register(require('fastify-cookie'), (err) => {
    if (err) throw err
  });

  fastify.addHook('preHandler', (request, reply, done) => {
    if(request.cookie) { // always fails
      done();
      return;
    }
    reply.code(403).send({
      error: 'Unauthorized',
      message: 'No cookie',
      statusCode: 404
    });
  });

  fastify.post('/register', async (request, reply) => {
    // Cookie exists here
    const user = await User.create(request.body);
    return { data: user };
  });

I'm certain the cookie actually exists.
I know the middleware utilizes the preHandler to set the cookie object, is that the reason it can't be accessed inside any other preHandler?

Exported types do not support using HTTP2

The plugin type exported here expects only HTTP underlying types while Fastify itself supports both HTTP and HTTP2 types.

This package seemingly works with either and can safely expect either one.

Allow passing "secret" as part of the `setCookie` and `getCookie` decorators

If you like the idea I'll have a PR ready in no time :)

🚀 Feature Proposal

Allow passing "secret" as part of the setCookie and getCookie decorators, not just at the plugin's initialization.

Motivation

Allow the usage of multiple secrets according to application's logic.

One example is using a different secret per logged in user.

Example

Usage example:

fastify.get('/', (req, reply) => {
  const aCookieValue = req.cookies.cookieName
  const bCookieValue = reply.unsignCookie(req.cookies.cookieSigned, 'my-secret');
  reply
    setCookie('bar', 'bar', {
      path: '/',
      signed: true,
      secret: 'my-secret'
    })
    .send({hello: 'world'})
})

Changes to code:

value = cookieSignature.sign(value, opts.secret || secret)  // line: 14
// lines: 59-61
fastify.decorateReply('unsignCookie', function unsignCookieWrapper (value, secretOverride) {
  return cookieSignature.unsign(value, secretOverride || secret)
})

Action required: Greenkeeper could not be activated 🚨

🚨 You need to enable Continuous Integration on all branches of this repository. 🚨

To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.

Since we didn’t receive a CI status on the greenkeeper/initial branch, it’s possible that you don’t have CI set up yet. We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.

If you have already set up a CI for this repository, you might need to check how it’s configured. Make sure it is set to run on all new branches. If you don’t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/.

Once you have installed and configured CI on this repository correctly, you’ll need to re-trigger Greenkeeper’s initial pull request. To do this, please delete the greenkeeper/initial branch in this repository, and then remove and re-add this repository to the Greenkeeper App’s white list on Github. You'll find this list on your repo or organization’s settings page, under Installed GitHub Apps.

`options.secure` is missing typing for "auto", only `boolean` is allowed

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

4.5.3

Plugin version

8.1.0

Node.js version

16,17.0

Operating system

Linux

Operating system version (i.e. 20.04, 11.3, 10)

Ubuntu 20.04.4 LTS

Description

Currently a Typescript error is thrown when one sets the options.secure flag to "auto" according to the docs

Steps to Reproduce

Simply set secure = "auto" and you'll get the error as the secure option is typed as boolean as seen below on line 10:
image

Expected Behavior

No type error?

Show warning when cookie size is over 4K (general browser size limit)

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the feature has not already been requested

🚀 Feature Proposal

Currently when you send a too large cookie it just gets ignored by the browser without warnings - this makes figuring out why it's not set really cumbersome (took me almost an hour to realise this)

So maybe instead of the browser giving you the warning, fastify-cookie can give one instead of the go over this safe limit?

See: http://browsercookielimits.iain.guru/

Motivation

Painful to debug why a cookie is not being set as no warnings are emitted anywhere and it's hard to guess that you reached this size

Example

Just a warning messing using FastifyInstance.log.warn that you exceeded the safe limit (with an option to turn it off maybe)

An in-range update of @types/node is breaking the build 🚨

The devDependency @types/node was updated from 12.7.0 to 12.7.1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@types/node is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build could not complete due to an error (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Add type definitions for parseOptions in FastifyCookieOptions

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the feature has not already been requested

🚀 Feature Proposal

Currently, parseOptions is not defined under FastifyCookieOptions. I was hoping to change the FastifyCookieOptions interface to the following:

// plugin.d.ts

export interface FastifyCookieOptions {
  secret?: string | string[];
  parseOptions?: CookieSerializeOptions;
}

And add the following test:

// test/plugin.d.ts

const appWithParserOptions = fastify();

const parseOptions: fastifyCookieStar.CookieSerializeOptions = {
  domain: "example.com",
  encode: (value: string) => value,
  expires: new Date(),
  httpOnly: true,
  maxAge: 3600,
  path: "/",
  sameSite: "lax",
  secure: true,
  signed: true,
};
expectType<fastifyCookieStar.CookieSerializeOptions>(parseOptions);

appWithParserOptions.register(cookie, {
  secret: "testsecret",
  parseOptions,
});
appWithParserOptions.after(() => {
  server.get("/", (request, reply) => {
    const { valid, renew, value } = reply.unsignCookie(request.cookies.test);

    expectType<boolean>(valid);
    expectType<boolean>(renew);
    expectType<string | null>(value);
  });
});

Motivation

No response

Example

No response

Error ts2392 occurs in the TypeScript environment due to the wrong return type of unsignCookie

🐛 Bug Report

Error ts2392 occurs in the TypeScript environment due to the wrong return type of unsignCookie

To Reproduce

const result = reply.unsignCookie(request.cookies[key])
if (result.valid) { ... }

Error ts2392 occurred with result.valid.

Expected behavior

needs to update plugin.d.ts

unsignCookie(
value: string,
): string | false;

Your Environment

  • node version: 14
  • fastify version: >=3.9.2
  • os: Linux
  • fastify-cookie: "5.0.0"
  • typescript: "4.1.3"

Validation of cookies

Hello! I'm using fastify-cookie for my project to work with cookies and I have a question about the route validation. Is it possible to express in a schema to specific what exact cookie should be in the cookie header?

What I want: we have the header 'cookie' that must exist, where the cookie name 'id' must exist and be typof number.

As of now, I can only specify that the header 'cookie' must exist, but sadly I don't know if it's possible to check for certain keys in such header and I was wondering if this is possible or not.

Thank you!

An in-range update of standard is breaking the build 🚨

The devDependency standard was updated from 14.3.0 to 14.3.1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

standard is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Commits

The new version differs by 21 commits.

  • 0ff9ace 14.3.1
  • b52a17f Create .tidelift.yml
  • 217d2be changelog
  • 7886564 Don't run on Node.js versions less than 8.6.0 (#1418)
  • 4869b15 Update Japanese documents to follow original documents (#1417)
  • 264db54 Don't run on Node.js versions less than 8.6.0
  • 03d56a9 Update Japanese document for 2c9be54
  • fd5db17 Update Japanese document for 600880d
  • 9dd6012 Update Japanese document for 7c38176
  • bc9d332 Update Japanese document for d195586
  • 8be039b Update Japanese document for b28fa54
  • b0e5e62 Update Japanese document for b8c7b33
  • d1fcbf4 Update Japanese document for ca5ac99
  • 300753d Update Japanese document for b83c629
  • 86bbd1e Update Japanese document for c003b84

There are 21 commits in total.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

I see set-cookie in response headers, but cookie is not saved in browser

Basically after an auth, I setting a cookie, but apparently after page refresh on the cookie that was set by cloudflare is saved And the cookie that I transmitted with set-cookie is not used in after set-cookie requests

# Response headers
HTTP/2.0 200 OK
date: Thu, 18 Jul 2019 10:03:25 GMT
content-type: application/json; charset=utf-8
content-length: 29
set-cookie: __cfduid=d578c7a5e4378dc1b1946964a08ebc4ec1563444205; expires=Fri, 17-Jul-20 10:03:25 GMT; path=/; domain=.doc.io; HttpOnly; Secure
set-cookie: __doc=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlIjoiNzQ2NTczNzQ0MDY2NjE3Mzc0NmQ2MTY5NmMyZTZkNzgiLCJwIjoiNTUzMjQ2NzM2NDQ3NTY2YjU4MzEzODMzNTU0NjUwNTU2ZjRiMzkzMTY3NDUzNDY5NDc3MzM3MzgzOTU5MzczMDUxNjk0ZjQxNjQ0OTM5Nzg0YjZiNzU1Njc3Nzk0NDc0NjE3NDMxNTE0NzcwMzE0YjQxNmY1MjU5MzM3YTZhNDU2NDJiNmU0ZTc0NGE3NTMyNTQ1ODc2NjI1YTczNDc1MTQ1Njc0MjVhNGQ0MTNkM2QiLCJkIjoiMzEzNTM2MzMzNDM0MzQzMjMwMzUzNTM2MzMiLCJpYXQiOjE1NjM0NDQyMDV9.go1jDpc2rBe5FjK2sKX4ybW4PhCPFq1xT1WIX-mSI84; Domain=.doc.io; Path=/; Expires=Thu, 18 Jul 2019 16:03:25 GMT; HttpOnly; Secure
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
server: cloudflare
cf-ray: 4f83a02a3dc36455-FRA
X-Firefox-Spdy: h2
reply
.code(200)
.header('Access-Control-Allow-Origin', '*')
.header('Content-Type', 'application/json; charset=utf-8')
.setCookie('__doc', token, {
domain: '.doc.io',
path: '/',
secure: true,
httpOnly: true,
expires: new Date(new Date().setHours(new Date().getHours() + 6))})
.send({ 'success': 'Sign In success' })

All my websites are https

First I do POST request for an auth on /auth, and you could see response in response headers above and after I do GET on (trying to load page) from /page and get cookies, but with reply.log.info(request.cookies) I see only cookies from cloudflare. Surely I tried to refresh and go to address in different table, there just no any cookies, but from cloudflare.

# Request headers
Host: test.doc.io
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
DNT: 1
Connection: keep-alive
Cookie: __cfduid=d578c7a5e4378dc1b1946964a08ebc4ec1563444205
Upgrade-Insecure-Requests: 1
TE: Trailers

P.S.: I removed httpOnly: true as it seems shouldn't work for json request types, however that changes nothing

Any idea?

Using this plugin makes app unusable

When i am using this app, this error appears and doesn't goes next.

FastifyError [FST_ERR_CTP_INVALID_CONTENT_LENGTH]: FST_ERR_CTP_INVALID_CONTENT_LENGTH: Request body size did not match Content-Length
       at IncomingMessage.onEnd (/Users/dalisoft/Desktop/Projects/uxcandy-test/node_modules/fastify/lib/contentTypeParser.js:177:18)
       at IncomingMessage.emit (events.js:198:15)
       at endReadableNT (_stream_readable.js:1142:12)
       at processTicksAndRejections (internal/process/task_queues.js:81:17)
     name: 'FastifyError [FST_ERR_CTP_INVALID_CONTENT_LENGTH]',
     code: 'FST_ERR_CTP_INVALID_CONTENT_LENGTH',
     message:
      'FST_ERR_CTP_INVALID_CONTENT_LENGTH: Request body size did not match Content-Length',
     statusCode: 400 }

How my code looks

// src/server.js
const fastify = require('fastify');
const app = fastify({
  // logger: true,
});

if (process.env.NODE_ENV === 'development') {
  require('dotenv-safe').config();
}

require('./middlewares/developer-verify')(app)
    .register(require('fastify-cookie'))
    .register(require('fastify-multipart'))
    .register(require('./register-routes'));

// Run the server!
const start = async () => {
  try {
    await app.listen(process.env.PORT || 8080, '0.0.0.0');
    console.log(`Server listening on ${app.server.address().port}`);
  } catch (err) {
    console.error(`Server listening error`, err);
    process.exit(1);
  }
};
start();
// middlewares/developer-verify.js
const {devName} = require('../config');

module.exports = (app) => {
  app.addHook('onRequest', async (req, res, next) => {
    const method = req.raw.connection.parser.incoming.method;
    if (method !== 'GET') {
      return next();
    }
    if (!req.query.developer || req.query.developer !== devName) {
      res.status(403);
      return res.send({
        'status': 'error',
        'message': 'Не передано имя разработчика',
      });
    }
    next();
  });
  return app;
};
// register-routes.js
module.exports = (app, options, next) => {
  // Error handling for more information
  app.setErrorHandler(async (error, request, res) => {
    console.log('Error was happened', {
      error,
      request,
    });

    return {
      status: 'error',
      message: 'The error was happened',
    };
  });

  // Root route
  app.get('/', (req, res) => {

});
next();

An in-range update of tap is breaking the build 🚨

The devDependency tap was updated from 12.5.0 to 12.5.1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

tap is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Commits

The new version differs by 4 commits.

  • d03f383 12.5.1
  • 2816d28 Ignore esm in stack output
  • 7daf018 do not run browser test in node v6
  • a51cc6f run test: load tap from full module location

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

The "latest" tag on npm is currently pointing to v5.5.0

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

3.27.2

Plugin version

5.6.0

Node.js version

16

Operating system

Linux

Operating system version (i.e. 20.04, 11.3, 10)

20.04

Description

It should be pointing to the latest version 5.6.0, no?

Steps to Reproduce

$ npm info fastify-cookie

-- removed for brevity --

dist-tags:
latest: 5.5.0  next: 5.6.0    

Expected Behavior

$ npm info fastify-cookie

-- removed for brevity --

dist-tags:
latest: 5.6.0

Remove `request` dependency

The request library has been deprecated and (essentially) abandoned for a long time. We should rely on another library like got for testing (or simply use fastify.inject if possible).

Add fastify.unsignCookie to be able to verify fastify.parseCookie values

🚀 Feature Proposal

Add fastify.unsignCookie

Motivation

#75 added fastify.parseCookie to mitigate fastify/fastify-websocket#70 and it works, but there is no easy way to verify a signed cookie, so its usefulness is limited.

The workaround now is to create a custom signer that dupes the default. But that's less than ideal.

Example

// fastify-websocket handler
const signedSessionId = fastify.parseCookie(req.headers['cookie']);
const sessionId = fastify.unsignCookie(signedSessionId);
ok(sessionId.valid);
console.log('session id:', sessionId.value);

Setting a specific path via the options argument isn't applied.

Example:

  1. res.setCookie('language', 'en', { path:'/foo' });
  2. res.setCookie('language', 'en', { path:'/' });

Expected outcome:

  1. Name language value en path /foo
  2. Name language value en path /

Actual outcome:

  1. No cookie is getting set at all.
  2. Default path is taken

Tested in Chrome Version 67.0.3396.99
Node version v11.0.0
Fastify-Cookie Version 2.1.1

Property 'cookies' does not exist on type FastifyRequest

My simplified code is:

import fastify from './git/fastify'
import fastifyCookie from './git/fastify-cookie'
const server = fastify();
server.register(fastifyCookie).after( () => {
	server.get("/ping", async (request, reply) => {
		const cookies = request.cookies; // Property 'cookies' does not exist on type 'FastifyRequest<Server, IncomingMessage, RequestGenericInterface>'
		reply.clearCookie('foo'); // Property 'clearCookie' does not exist on type 'FastifyReply<Server, ServerResponse, unknown>'
	});
});

Where I have two errors:

  • Property 'cookies' does not exist on type 'FastifyRequest<Server, IncomingMessage, RequestGenericInterface>'
  • Property 'clearCookie' does not exist on type 'FastifyReply<Server, ServerResponse, unknown>'

I just followed by: https://github.com/fastify/fastify-cookie
Why it doesn't work?

  • node version: 13
  • fastify version: 3.0
  • fasify-cookie version: 3.6.0
  • os: Linux

ChainAlert: npm package release (5.7.0) has no matching tag in this repo

Dear fastify-cookie maintainers,
Thank you for your contribution to the open-source community.

This issue was automatically created to inform you a new version (5.7.0) of fastify-cookie was published without a matching tag in this repo.

As part of our efforts to fight software supply chain attacks, we would like to verify this release is known and intended, and not a result of an unauthorized activity.

If you find this behavior legitimate, kindly close and ignore this issue. Read more

badge

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.