Coder Social home page Coder Social logo

jlalmes / trpc-openapi Goto Github PK

View Code? Open in Web Editor NEW
2.0K 9.0 127.0 5.14 MB

OpenAPI support for tRPC 🧩

Home Page: https://www.npmjs.com/package/trpc-openapi

License: MIT License

JavaScript 0.66% TypeScript 99.34%
openapi swagger trpc nodejs rest typescript

trpc-openapi's People

Contributors

aarthit avatar anthonyshew avatar aphex avatar bahnasawy avatar dependabot[bot] avatar elonsalfati avatar jlalmes avatar klapacz avatar mamatsunami avatar mrgoonie avatar seancassiere avatar timmattison avatar trmanderson 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

trpc-openapi's Issues

RFC: Add Model Schemas to OpenApi Docs

First, I'm loving the project so far and I really appreciate all the work you've done already.

I honestly don't mind the output requirement on a query/mutation, however, it would be nice to be able to define Model Schema's and reference those for the outputs inside the docs. (ex. Pet Model from Redoc).

express example problems out of the box

on compile there are errors about input schemas
TRPCError: [mutation.auth.register] - Input parser expects a Zod validator

also, the line app.use('/api', createOpenApiExpressMiddleware({ router: appRouter, createContext })); produces this error

TS2345: Argument of type '{ router: CreateRouterInner<RootConfig<{ ctx: Context; meta: OpenApiMeta<TRPCMeta>; errorShape: DefaultErrorShape; transformer: DefaultDataTransformer; }>, { ...; }>; createContext: ({ req, res, }: CreateExpressContextOptions) => Promise<...>; }' is not assignable to parameter of type 'CreateOpenApiExpressMiddlewareOptions<Router<TContext, TContext, OpenApiMeta<TMeta>, any, any, any, DefaultErrorShape>>'.   Type '{ router: CreateRouterInner<RootConfig<{ ctx: Context; meta: OpenApiMeta<TRPCMeta>; errorShape: DefaultErrorShape; transformer: DefaultDataTransformer; }>, { ...; }>; createContext: ({ req, res, }: CreateExpressContextOptions) => Promise<...>; }' is missing the following properties from type 'CreateOpenApiExpressMiddlewareOptions<Router<TContext, TContext, OpenApiMeta<TMeta>, any, any, any, DefaultErrorShape>>': responseMeta, onError, teardown, maxBodySize

Support for ZodEffects

Hi! I got following error when using zod with refine effect as input.

[mutation.okInput] - Input parser must be a ZodObject

Reproducible example.

const appRouter = trpc.router<any, OpenApiMeta>().mutation('okInput', {
  meta: { openapi: { enabled: true, path: '/ok-input', method: 'POST' } },
  input: z
    .object({
      a: z.string(),
      b: z.string(),
    })
    .refine((data) => data.a === data.b),
  output: z.null(),
  resolve: () => null,
});

const openApiDocument = generateOpenApiDocument(appRouter, {
  title: 'tRPC OpenAPI',
  version: '1.0.0',
  baseUrl: 'http://localhost:3000/api',
});

Feat: Support top-level `z.preprocess`

Currently z.preprocess throws the following error Input parser must be a ZodObject:

{
  input: z.preprocess(
    camelize,
    z.object({
      createdAt: z.string(),
      deliveryInfo: z.object({
        currentRetry: z.number(),
        maxRetries: z.number(),
      }),
      event: z.object({
        data: z.object({
          new: data,
          old: data.nullish(),
        }),
        op: z.string(),
        sessionVariables: z.record(z.string().regex(/x-hasura-.*/), z.string()),
        traceContext: z.object({ spanId: z.string(), traceId: z.string() }),
      }),
      id: z.string(),
      table: z.object({ name: z.string(), schema: z.string() }),
      trigger: z.object({ name: z.string() }),
    }),
  )
}

fix/support multiple tags per endpoint

I was trying out the package and noticed that each endpoint is limited to a single tag.
Please see below.

const testRouter = createRouter().query("hello", {
	meta: { openapi: { enabled: true, method: "GET", path: "/hello", tag: "OnlyASingleTag" } },
	input: z.object({ /** input stuff */ }),
	output: z.object({ /** output stuff */ }),
	async resolve({ input }) {
		/** implementation stuff */
	},
});

Maybe this needs to change? Since tags are used to group operations together and a single one could be in two groups. Also, Swagger Docs on the OpenAPI Spec for 3.0.3, states that the tag field should container an Array of string tags. -> string[]
https://swagger.io/specification/#operation-object

Feat: tRPC V10 Support

Hi James,
Is the tRPC alpha version 10 already supported? If not, when would it be possible?
Also since this package is so closely tied and useful to tRPC, has it been considered to be merged into the tRPC monorepo?

Refactoring express example to thr own router file causes weird error

@jlalmes

Awesome work on this package. I am getting this weird issue, take a look.

I am trying to take out for example here postsProtectedRouter to its own file. But typescript is yelling at me.

export const postsProtectedRouter = createProtectedRouter()
                                                         ^
TypeError: (0 , index_1.createProtectedRouter) is not a function

🚘 v1.0.0 Roadmap

🚘 v1.0.0 Roadmap

🏠 House keeping

  • Write v0 -> v1 migration guide. (currently maintained here #143)

🚀 Definite

  • Support tRPC v10. (#73)
  • Remove response wrapper (#144)
  • Add CORS setup to examples. (#122)
  • Deprecate meta.tag (use meta.tags instead). (#92)
  • Default to enabled: true when meta.openapi is defined. (#68)
  • Fix YAML file $refs. (#37)
  • Allow method: 'DELETE' in mutation procedures. (#123)
  • Add meta.headers. (#113)
  • Support z.refine(). (#102)
  • Performance improvements. (#125)
  • #156

🤔 Maybe

  • Support z.number() query input. (#44)
  • Support z.boolean() query input. (#44)
  • Support z.date() query input. (#44)
  • Support z.array() query inputs. (modtree/modtree#358 (comment))
  • Add model schemas to OpenAPI document. (#157)
  • Add Fastify adapter. (#87)
  • Add Lambda adapter. (#115)
  • Add meta.examples.
  • Add override object to OpenApiMeta and GenerateOpenApiDocumentOptions.

🏋️‍♀️ Stretch

  • Support non-z.object() input.
    • Input is mandated as z.object() if using pathParameters.
    • Query input should be ?input=.
  • Remove output schema requirement. (#61 (comment))
  • Remove zod validator requirement.

RFC: Support `number`, `boolean`, `Date` in query params

Right now it's not possible to have z.number()'s in your input schema for GET requests.
That makes sense since query params are strings (so you get an error like Input parser key: "limit" must be ZodString).

But often (I'm thinking especially of pagination), you need numbers. Right now, you can recreate similar functionality with z.string().transform((s) => parseInt(s), but it's not ideal since you lose, for example, Zod's built-in min/max validation.

At a minimum, this deserves a mention in the documentation. If we're feeling more ambitious, we can introduce autoconversions to number/date for common types.

I'm happy to open a PR, but first wanted to hear what you think, @jlalmes.

OpenApi Document Generation

First thanks for the great work,

When I generate an open api document. is there a way to add documentation for the input,

E.g

    input: z.object({
      name: z.string().openapi({
        example: 'John Doe',
    }),

Thanks

Lack of support for valid endpoints

Consider the following code from Usage with Express

getUser: t.procedure.input(z.string()).query((req) => {
  req.input; // string
  return { id: req.input, name: 'Bilbo' };
}),

After adding this line

.meta({ openapi: { method: 'GET', path: '/getUser' } })

trpc-openapi fail to process it

TRPCError: [query.getUser] - Input parser must be a ZodObject

Typescript response (instead zod) to Openapi

In TRPC I only need to use zod (or yup) for the input, but the output type gets automatically passed on to the frontend without a need for zod.
I want to make an endpoint for a function that already has a typed response. I would rather not have to rewrite all types into zod.

Defining procedures with no `input`

Defining a query/mutation with no input results in

TRPCError: [query.<queryName>] - Input parser expects a Zod validator

It's possible to workaround this by defining an empty input:

input: z.object({}),

However, that way an empty object is expected when calling the query via TRPC client.
It's a legit use-case to have queries/mutations with no input, right? Can we allow omitting input in such cases?

Path params

Hey @jlalmes, this is a great little library!

I am exploring possibilities and as far as I can understand, there is currently no way to define a path parameter.
Consider the petstore swagger example, the uploadFile has petId parameter, which is part of the path.

Could we try to extract the path params by looking for curly braces in meta.openapi.path, and then find param with the same name in input and mark that param as from: "path" instead of from: "query"?

WIP attempt to implement the above approach: v0.1.0...dodas:feat/path-parameters

Have you thought about supporting this use case?
I am happy to discuss this and potentially take a stab at implementing it.

Thanks!

Missing explanation for running the express example project

Missing explanation for running the express example project

Missing build script

When trying to use the trpc-openapi with express example project you will get an error saying that the script for building the project is missing.

image

And when looking into the package.json it seems to be missing:

"scripts": {
"dev": "ts-node-dev --respawn --transpile-only --exit-child ./src/index.ts"
},

Missing trpc-openapi

When running NPM run dev and npm run dev -w with-express you will get the following error:
image

Missing main entry

After installing the TRPC-openapi it will throw the following error:
image

Solution:

Can someone who knows how to run this project update the READ.ME of the with express example?
I suspect that I am missing a step that is necessary for running the example project.

https://github.com/jlalmes/trpc-openapi/blob/master/examples/with-express/README.md

Reduce needed meta

Is your tool intented to be used as just an API endpoint? If so, would it be possible to reduce the meta openapi complexity, and just enable openapi for all routes?

openapi: {
        enabled: true,       //default from global settings
        method: "POST",      //default GET for query and POST for mutation
        path: "/auth/login", //defaults to TRPC route
        tag: "auth",
        summary: "Login as an existing user",
      },

Option to ignore output validation?

Output validation is required by trpc-openapi to generate the valid response schema, but then maybe we don't really want to enable runtime output validation for our procedures.

I'm not sure what would be the ideal solution here.

Is there any way to ignore the runtime output validation?

RFC: Remove response wrapper

hi!

i need to satisfy a REST call which i can't influence. is there a way to return the response without the wrapper:

instead of this:

{
  "ok": true,
  "data": "This is good" /* Output from tRPC procedure */
}

return this:

{
  "name": "trpc", /* Output from tRPC procedure */
  "lang": "TS" /* Output from tRPC procedure */
}

if not, it would be a nice enhancement since i'm going to be not the only one with this issue. i'm probably going to fork this for now until i'm confident enough to make a PR.

const successResponseObject = {
    description: 'Successful response',
    content: {
      'application/json': {
        schema: zodSchemaToOpenApiSchemaObject(
          z.object({
            ok: z.literal(true),
            data: schema,
          }),
        ),
      },
    },
  };
```

Defaults for meta.openapi.method and meta.openapi.path

I think to make adoption easier meta.openapi.method and meta.openapi.method can be optional:

  • meta.openapi.method is GET for query and POST for mutation. Making deletion with POST is not orthodox but its better to make it optional
  • meta.openapi.path can be the mutation/query name. I prefer /say-hello to /sayHello too, but having the former as an option also makes adoption easier.

Thoughts?

Understanding compression compared to normal openapi

Hi thanks for the awesome package!

I am still a beginner using tRPC and am using this package to incrementally add a trpc layer into my projects.
I was wondering if there is a possibility of using compression algos such as gzip to the body response.
I understand that data transformers are not supported, but is this out of scope due to how trpc would compress as discussed here?

It seems like something that isn't really applicable to normal tRPC

`input: z.void()` conflicts with trpc client

Not sure what's exactly is going on, but when using trpc-openapi with a procedure that has input: z.void() (or z.undefined(), or z.never()), I am no longer able to use this procedure via TRPC Client, it fails with following error:

[
  {
    "code": "invalid_type",
    "expected": "object",
    "received": "undefined",
    "path": [],
    "message": "Required"
  }
]

Just setting openapi.enabled to false (while keeping input: z.void()) fixes this and I am able to use the given procedure with no input.
Enabling openapi for given prodedure breaks usage with TRPC Client.

`ZodEnum` and `ZodNativeEnum` as `input` param

Can we support ZodEnum and ZodNativeEnum types as input params?
Both of these accept string as their input, so they should be compatible, right?

It currently throws with

TRPCError: [<procedureName>] - Input parser key: "<propName>" must be a ZodString

Feat: AWS Lambda handler

I'm currently trying to set this up with AWS Lambda do you think the node:http adaptor would be enough to set this up?

Feat: Support for other `Content-Types`

As of now trpc-openapi does not support sending a response using other Content-Types other than "application/json" is there any workaround for that? or i did something wrong here.

Path params should be omitted from request body params

Currently, using path params in a mutation results in a openAPI schema where the given param is expected in both requestBody and parameters. Instead, I would expect the given param to be omitted from the request body schema, since it's passed via path.

For example, in the method below I would expect blueprintId to be passed via path and Request body only containing credentialId.
Screenshot 2022-06-08 at 00 11 18

Getting Error : Input parser key: "from" must be ZodString

Hey thanks for this library,

I am trying to understand why the value of an input objects must be a ZodString. I get following error : Input parser key: "from" must be ZodString

  input: z.object({
        from: z.number(),
        to: z.number(),
    }),

I read here https://github.com/jlalmes/trpc-openapi/blob/next/README.md that the input object is defined as ZodObject<{ [string]: ZodString }> but openApiSpec also allows number/ integer etc

at least number would be good.

Schema $refs are not pointing correctly

Hi there,

Thank you for taking the time to put this library together! I just came across an issue when opening the swagger UI - I see errors like so:

Could not resolve reference: #/properties/data/properties/id

From looking through the source, it looks like the zod-to-json-schema refStrategy (root is the default) is not mapping correctly. Maybe because the entire object is not being passed into the function?

How to use with Prisma?

Hello,

First, thanks for your library!

I use trpc with Prisma and I have a type problem when I define the output. I don't know if it is a problem from trpc-openapi

my schema.prisma

...

model User {
    id            String    @id @default(cuid()) @map("_id")
    name          String?
    email         String?   @unique
    emailVerified DateTime?
    image         String?
}

my users.ts router

import { createRouter } from "@server/router/context";
import { z } from "zod";

export const usersRouter = createRouter()
  .query("getAll", {
    meta: {
      openapi: {
        enabled: true,
        method: 'GET',
        path: '/users',
        tag: 'users',
        summary: 'Read all users'
      }
    },
    input: z.void(),
    output: z.object({
      id: z.string()
    }),
    async resolve ({ctx}) {
      return await ctx.prisma.user.findMany()
    }
  });

If someone have an idea, how to resolve my issue?

TRPCError: Input parser must be a ZodObject

I am getting the following error: TRPCError: Input parser must be a ZodObject

I have looked through all the previous issues and can't seem to figure out what I am doing wrong. I have one route

const createRouter = () => trpc.router<Context, OpenApiMeta>();

export const appRouter= createRouter().query("hello", {
  meta: { openapi: { enabled: true, method: "GET", path: "/hello" } },
  input: z
    .object({
      text: z.string().nullish(),
    })
    .nullish(),
  output: z.object({ greeting: z.string() }),
  resolve({ input }) {
    return {
      greeting: `Hello ${input?.text ?? "world"}`,
    };
  },
});

and as you can see I do have zod as the input parser.

error - Error: Cannot find module 'tslib'

On both examples with-nextjs and with-express I'm getting this error.
Anyway: Super cool project!

error - Error: Cannot find module 'tslib'
Require stack:
- /home/martin/node_modules/.pnpm/[email protected]_5zkoywanuxf5zpyqjjt34qanl4/node_modules/trpc-openapi/dist/adapters/index.js
- /home/martin/node_modules/.pnpm/[email protected]_5zkoywanuxf5zpyqjjt34qanl4/node_modules/trpc-openapi/dist/index.js
- /home/martin/workspace/api-repo/with-nextjs/.next/server/pages/api/openapi.json.js

Reproduce:

Open directory with with-nextjs:

  • pnpm install
  • pnpm dev

Solution:

  • pnpm add trpc-openapi

[Help wanted] T3 Stack and Queryparameters

Hey Guys

I've started using the T3 Stack with trpc / nextjs and wanted to use this awesome package to my Project.
Everything works fine and I see the OpenAPI-Docs in the expected route. But i dont understand why this happens

When looking at my trpc query, I have the following:

export const exampleRouter = createRouter().query("hello", {
  meta: { openapi: { enabled: true, method: "GET", path: "/api/trpc/example.hello" } },
  input: z.object({ name: z.string() }),
  output: z.object({ greeting: z.string() }),
  resolve: ({ input }) => {
    return { greeting: `Hello ${input.name}!` };
  },
});

which generated the OpenAPI docs but when i try to execute it throught the UI it generates the URL like this:
http://0.0.0.0:3000/api/trpc/example.hello?name=asdf which is of course not wrong. But when looking into the dev-tools on my page the request generated from trpc-client is generated like this:
http://localhost:3000/api/trpc/example.hello?batch=1&input={"0":{"json":{"name":"jerome"}}}

Can i change this behavior for the generation of the OpenAPI docs or how would you recommend trying to fix this?

Thanks in advanced
Jerome

`z.instanceof()` is ignored

Consider the following code

class User {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

const user = new User('John');

export const trpcRouter = t.router({
  getUser: t.procedure
    .input(
      z.object({
        userId: z.number(),
      }),
    )
    .output(z.instanceof(User))
    .meta({ openapi: { method: 'POST', path: '/user' } })
    .mutation(() => user),
});

Expected output schema

"schema": {
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    }
  },
  "required": [
    "name"
  ],
  "additionalProperties": false
}

What we actually have

"schema": {}

TRPCError: Input parser must be a ZodObject

Hi,
I am getting the following error: TRPCError: Input parser must be a ZodObject. I am following the nextjs example provided in trpc-openapi/src/examples. Getting the same error on all trpc-open api routes [...trpc] including the openapi.json route.

I have looked through all the previous issues and can't seem to figure out what I am doing wrong.

Please let me know if additional details are required.

Regards,
Dawood

Package.json:

{
"name": "next-boilerplate",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@tanstack/react-query": "^4.20.2",
"@trpc/client": "^10.5.0",
"@trpc/next": "^10.5.0",
"@trpc/react-query": "^10.5.0",
"@trpc/server": "^10.5.0",
"@types/styled-components-react-native": "^5.1.3",
"next": "12.1.6",
"nextjs-cors": "^2.1.2",
"react": "18.1.0",
"react-dom": "18.1.0",
"react-is": "^18.2.0",
"styled-components": "^5.3.5",
"superjson": "^1.12.0",
"swagger-ui-react": "^4.15.5",
"trpc-openapi": "^1.0.0",
"zod": "^3.20.2"
},
"devDependencies": {
"@types/node": "17.0.36",
"@types/react": "18.0.9",
"@types/react-dom": "18.0.5",
"@types/styled-components": "^5.1.25",
"@types/swagger-ui-react": "^4.11.0",
"eslint": "8.16.0",
"eslint-config-next": "12.1.6",
"typescript": "4.7.2"
},
"resolutions": {
"styled-components": "^5"
}
}

Confused how to get swagger to call endpoint correctly

export const campaign = router({
  findOne: publicProcedure
    .meta({
      openapi: { 
        method: 'GET',
        path: '/campaign/findOne',
        description: 'Will return a individual admanager campaign by id'
      }
    })
    .input(z.object({ id: z.string() }))
    .output(Campaign)
    .query(({ input }) => {
      console.log('input', input);
      return { 
        id: '0a1ee26ae7c739472a9ca53810a661d1',
        name: `campaign name: ${input.id}`
      };
    }),
    
});

The above works fine with the client by using: trpc.campaign.findOne.useQuery({ id: 'xyz12374574' });

However, I've tried changing the path to /campaign.findOne & /campaign.findOne/{id} & /campaign.findOne?id={id} but no success, seems to not format the url correctly to resolve the response, any idea why?

[\n  {\n    \"code\": \"invalid_type\",\n    \"expected\": \"object\",\n    \"received\": \"undefined\",\n    \"path\": [],\n    \"message\": \"Required\"\n  }\n]

This is the error I'm getting back in swagger

errorFormatter returns invalid openapi schema

I've been using errorFormatter to include Zod errors and more custom errors, but the returned
error type in openapi schema doesn't synchronize
trpc error formatter:

    errorFormatter({ shape, error }) {
        return {
            ...shape,
            data: {
                ...shape.data,
                errCode: error.cause instanceof CustomTRPCError ? error.cause.errCode : null,
                zodError:
                    error.code === 'BAD_REQUEST' &&
                        error.cause instanceof ZodError
                        ? error.cause.flatten()
                        : null,
            },
        };
    }

return open-api schema:

{
  "message": "string",
  "code": "string",
  "issues": [
    {
      "message": "string"
    }
  ]
}

real response:

{
  "message": "Input validation failed",
  "code": "BAD_REQUEST",
  "issues": [
    {
      "validation": "uuid",
      "code": "invalid_string",
      "message": "Invalid uuid",
      "path": [
        "keyRingKeyId"
      ]
    }
  ]
}

RFC: Reduce OpenAPI `meta` complexity

Initially proposed by @mshd (#55)
Additionally raised by @theobr (https://youtu.be/YAzzvhaRs6M?t=7086)

RFC: Reduce OpenAPI meta complexity

It has been mentioned a few times that the meta required to enable OpenAPI support on a tRPC procedure could be reduced. This RFC proposes setting some sensible defaults (can be overwritten), such that the minimum required meta will be as follows:

{ openapi: { enabled: true } }

Current requirements

  • meta.openapi.enabled: true
  • meta.openapi.method: GET | POST | PUT | PATCH | DELETE
  • meta.openapi.path: string

Sensible defaults

  • meta.openapi.enabled: defaults to false
  • meta.openapi.method: defaults to GET (query) | POST (mutation)
  • meta.openapi.path: defaults to computed value from tRPC procedure where:
    • nested routers = /
    • path = path.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase())

Example (v10)

const postsRouter = t.router({
  getById: t.procedure
    .meta({ openapi: { enabled: true } }),  // { openapi: { enabled: true, method: 'GET', path: '/posts/get-by-id' } }
    .input(z.object({ id: z.string() })),
    .output(postSchema)
    .query(({ input }) => {
       ...
    })
  ...
})

const appRouter = t.router({
  posts: postsRouter,
  ...
})

Concerns

  • The defaults will likely not follow proper RESTful API design patterns in most cases, examples:
    • /posts/get-by-id should be /posts/{id}.
    • update endpoints should be using PUT or PATCH.
  • It is not clear when you will be introducing a breaking changes to your public OpenAPI, example:
    • If you refactor a procedure name, its path will change.

Not fully sold on this change, any thoughts are welcomed (cc @KATT @sachinraja)

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.