GithubHelp home page GithubHelp logo

neg4n / next-api-og-image Goto Github PK

View Code? Open in Web Editor NEW
216.0 1.0 18.0 1.11 MB

:bowtie: Easy way to generate open-graph images dynamically in HTML or React using Next.js API Routes. Suitable for serverless environment.

Home Page: https://www.npmjs.com/package/next-api-og-image

License: MIT License

TypeScript 84.31% JavaScript 15.69%
nextjs nextjs-plugin react nextjs-serverless nextjs-api-routes nextjs-middleware next serverless hacktoberfest vercel typescript react-hooks reactjs html-templates jsx jsx-syntax html html5

next-api-og-image's Introduction

Next.js API OG Image · version types license

Simple library with purpose of providing easy way to dynamically
generate open-graph images using Next.js API routes.

If you're not familiar with dynamic open-graph images concept - please see vercel/og-image repository's README for very detailed explaination.

you can treat this project as simpler and configurable version of mentioned earlier vercel repository

Features

  • 🐄 Super easy usage
  • 🌐 Suitable for serverless environment
  • :bowtie: Elegant way for defining templates both in React and HTML
  • 📬 Multiple strategies - pass values by GET and query params or POST and JSON body
  • 🥷 TypeScript compatible

Installing

In your Next.js project, execute:

npm i next-api-og-image chrome-aws-lambda
# or
yarn add next-api-og-image chrome-aws-lambda

Short note about the peer dependencies

ℹ️ If your serverless function does not fit in the allowed size frames on Vercel (50MB), you may want to install older versions of chrome-aws-lambda

In order to do so, replace chrome-aws-lambda (while adding the dependencies) with [email protected] (47.6 MB)

Please, refer to #23 (comment) for more info 🙏

Examples

You can find more examples here:

the example/ directory contains simple Next.js application implementing next-api-og-image . To fully explore examples implemented in it by yourself - simply do npm link && cd example && npm i && npm run dev then navigate to http://localhost:3000/

Basic usage and explaination

HTML template
import { withOGImage } from 'next-api-og-image'

export default withOGImage({ template: { html: ({ myQueryParam }) => `<h1>${myQueryParam}</h1>` } })
React template
import { withOGImage } from 'next-api-og-image'

export default withOGImage({ template: { react: ({ myQueryParam }) => <h1>{myQueryParam}</h1> } })

Creating template

You've may noticed the html and react properties in configuration. Their responsibility is to provide HTML document to image creator (browser screenshot), filled with your values.

⚠️ NOTE
Template cannot be ambigious. You must either
define react or html. Never both at once

Specification

The html and react properties are template providers functions. Each function's first (and only) parameter is nothing else but HTTP request's query params converted to object notation.

This allows you to create fully customized HTML templates by simply accessing these parameters. The preferred way to do that is object destructuring.

⚠️ NOTE
html and react template provider functions
can be defined as asynchronous

Examples

HTML template
import { withOGImage } from 'next-api-og-image'

export default withOGImage({ template: { html: ({ myQueryParam }) => `<h1>${myQueryParam}</h1>` } })
React template
import { withOGImage } from 'next-api-og-image'

export default withOGImage({ template: { react: ({ myQueryParam }) => <h1>{myQueryParam}</h1> } })

if you send GET HTTP request to api route with code presented above e.g. localhost:3000/api/foo?myQueryParam=hello - it will render heading with content equal to 'hello'

Strategies

next-api-og-image allows you to choose strategy for providing values to the template. The available strategies are:

  1. query (default) - values are passed by query params and GET HTTP request.
    These values ⛔️ cannot be nested nor accessed by nested destructuring in template provider function.

  2. body - values are passed by POST HTTP request and JSON body.
    These values ✅ can be nested and accessed by nested destructuring in template provider function.

The strategies are determined by strategy prop in the configuration. Default strategy is query.

⚠️ NOTE
Regardless of the strategy - all properties (every single one)
are implicitly casted to string, even very long JSON's nested values

Types in template provider function

If you're using TypeScript, you probably want to have these things typed. Well... its actually super easy! Simply add generic types to withOGImage function.

  1. typed query strategy with query params ?foo=hello&bar=friend will look like this:
    export default withOGImage<'query', 'foo' | 'bar'>(/* ... */)
  2. typed body strategy with JSON payload { "foo": "hello", "imNested": { "bar": "friend" }} will look like this:
    export default withOGImage<'body', { foo: string, imNested: { bar: string } }>({ strategy: 'body', /* ... */ })

Errors

When strategy is set to query and you're sending POST HTTP request with JSON body or when strategy is set to body and you're sending GET HTTP request with query params - next-api-og-image will:

  1. Will throw an runtime error
  2. Set appropiate response message to the client You can disable this behaviour by setting dev: { errorsInResponse: false } in the configuration

Hooking the post-generate process

In some scenarios you may want to do something (in other words - execute some logic) after generation of the image. This can be easily done by providing function to hook configuration property. The only parameter is NextApiRequest object with image attached to it.

example (JavaScript):

import { withOGImage } from 'next-api-og-image'

export default withOGImage({
  template: {
    react: ({ myQueryParam }) => <div>🔥 {myQueryParam}</div>,
  },
  dev: {
    inspectHtml: false,
  },
  hook: (innerRequest) => {
    console.log(innerRequest.image)
    // will print the generated image on the server as Buffer
  },
})

Splitting files

Keeping all the templates inline within Next.js API route should not be problematic, but if you prefer keeping things in separate files you can follow the common pattern of creating files like my-template.html.js or my-template.js when you define template as react (naming convention is fully up to you) with code e.g.

export default function myTemplate({ myQueryParam }) {
  return `<h1>${myQueryParam}</h1>`
}

...or in TypeScript

import type { NextApiOgImageQuery } from 'next-api-og-image'

type QueryParams = 'myQueryParam'
export default function myTemplate({ myQueryParam }: Record<QueryParams, string>) {
  return `<h1>${myQueryParam}</h1>`
}

then importing it and embedding in the withOGImage.

Loading custom local fonts

In order to load custom fonts from the project source, you need to create source file with your font in base64 format or simply bind the font file content to the variable in your Next.js API route

Configuration

Apart from html and react configuration property (in template) (whose are required), you can specify additional info about how next-api-og-image should behave.

Example configuration with default values (apart from template.html or template.react prop):

const nextApiOgImageConfig = {
  // Values passing strategy
  strategy: 'query',
  // Response's 'Content-Type' HTTP header and browser screenshot type.
  type: 'png',
  // Screenshot's quality. WORKS ONLY IF 'type' IS SET TO 'jpeg'
  quality: 90,
  // Width of the image in pixels
  width: 1200,
  // Height of the image in pixels
  height: 630,
  // 'Cache-Control' HTTP header
  cacheControl: 'max-age 3600, must-revalidate',
  // Hook function that allows to intercept inner NextApiRequest with `ogImage` prop attached.
  // useful for e.g. saving image in the database after the generation.
  // The hook function return is Map containing custom headers that will be set BEFORE sending
  // response to the client.
  hook: null,
  // NOTE: Options within 'chrome' object only works when next-api-og-image is run in server (not serverless!!) environment.
  chrome: {
    // Custom command-line args passed to the browser start command
    // by default, no arguments are provided.
    args: null,
    // Custom executable provided. Useful when you e.g. have to run Chromium instead of Google Chrome
    // by default, executable is retrieved automatically (it looks for Google Chrome in the filesystem)
    executable: null,
  }
  // NOTE: Options within 'dev' object works only when process.env.NODE_ENV === 'development'
  dev: {
    // Whether to replace binary data (image/screenshot) with HTML
    // that can be debugged in Developer Tools
    inspectHtml: true,
    // Whether to set error message in response
    // if there are strategy related errors
    errorsInResponse: true,
  },
}

License

This project is licensed under the MIT license.
All contributions are welcome.

next-api-og-image's People

Contributors

bmstefanski avatar neg4n avatar snelsi avatar transitive-bullshit 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

next-api-og-image's Issues

using react templates outputs a string, not an image

Template type produces different outputs when hitting the API.

export default withOGImage({
  template: {
    html: ({ id }) => `
      <html>
        <head>
          <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
        </head>
        <body>
        <Sigil patp=${id} size={100}/>
          <h1 class="flex flex-row text-6xl text-blue-300">${id}<span>💻</span></h1>
        </body>
      </html>
    `,
  },
});

Produces an image.

export default withOGImage({
  template: {
    react: ({ myQueryParam }) => <div>🔥 {myQueryParam}</div>,
  },
});

Produces a text document. <div style={{color: 'blue'}}> for example produces blue text, so it isn't source code necessarily.

Unit tests

It would be great if next-api-og-image had 100% test coverage.

Some notes about tests:

  • We should use jest
  • There should be previously generated images in __stubs__ directory
    in order to set up further binary image output assertions
  • We should probably use supertest

Suggestion: move chrome-aws-lambda to peer dependency

Since the most recent version of chrome-aws-lambda is 49.7 MB, it leaves almost no room for additional client libs in the vercel lambda.

I've been running into this issue and it's really frustrating.

So my proposal is to switch chrome-aws-lambda to be a peer dependency and allow the user of this library to choose which chrome version they want to use.

See my other comment here for the savings we're talking about: #23 (comment)

This would require also making puppeteer-core a peer dependency, since the two versions need to match up.

Live example

Hi, is there a live example I could visit to check how the library works?
Also, it would prove the fact that this library can be deployed on Vercel without exceeding their quota :D
Let me know what you think and if I can help with something 😄

Failed to launch the browser process

image
Getting this error

Code Snippet

// ./api/og.js

import { withOGImage } from 'next-api-og-image';

export default withOGImage({
  template: { react: ({ title }) => <h1>{title}</h1> },
  dev: {
    inspectHtml: true,
  },
});

Happens when I enter /api/og?title=Hi

Running in Brave Browser
I have installed chrome as well but the path is different.

Simplify source code

The source code of next-api-og-image is needlessly complicated in many places. The goal is to make it simple without modifying the library's API.

Download the image

I've been trying to get it to work but having unfortunate errors.

My api/og-image.ts looks like:

import React from 'react'
import ReactDOM from 'react-dom/server'
import { withOGImage } from 'next-api-og-image'

import OGImagePreview from '@/pages/og-image-preview'
import { OGImage, Author } from '@/types/index'

const html = async ({
	title,
	description,
	date,
	readingTime,
	slug,
	authors,
}: Record<keyof OGImage, string | string[] | any[]>): Promise<string> => {
	const el = React.createElement(OGImagePreview, {
		title: title as string,
		description: description as string,
		date: date as string,
		readingTime: readingTime as string,
		slug: slug as string,
		authors: authors as Author[],
	})

	const body = ReactDOM.renderToStaticMarkup(el)

	const baseCSS = `
		.font-alegreya {
			font-family: Alegreya;
		}
		.font-jetbrains {
			font-family: 'JetBrains Mono';
		}
	`

	return `
		<html>
			<head>
				<meta charset="utf-8" />
				<link rel="preconnect" href="https://fonts.googleapis.com" />
				<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="true" />
				<link
					href="https://fonts.googleapis.com/css2?family=Alegreya:wght@800&display=swap"
					rel="stylesheet"
				/>
				<link
					href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&display=swap"
					rel="stylesheet"
				/>
				<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet" />
				<style>${baseCSS}</style>
			</head>
			<body>
				${body}
			</body>
			<script src="https://unpkg.com/tailwindcss-jit-cdn"></script>
		</html>
	`
}

const nextApiOgImageConfig = {
	// 'Content-Type' HTTP header
	contentType: 'image/png',
	// 'Cache-Control' HTTP header
	cacheControl: 'max-age 3600, must-revalidate',
	// NOTE: Options within 'dev' object works only when process.env.NODE_ENV === 'development'
	dev: {
		// Whether to replace binary data (image/screenshot) with HTML
		// that can be debugged in Developer Tools
		inspectHtml: true,
	},
	html,
}

export default withOGImage<keyof OGImage>(nextApiOgImageConfig)

How do I download the image itself?

I was trying to use simple fetch & also ky but not sure how to download it:

import ky from 'ky'

import {
	getAllEssays,
	getAllTutorials,
	getAllSnippets,
	getAllTips,
	getEssayBySlug,
	getTutorialBySlug,
	getSnippetBySlug,
	getTipBySlug,
} from '@/utils/mdx'
import { siteMetadata } from '@/_data/index'

const { siteUrl } = siteMetadata

export const generateOGImage = () => {
	try {
		const essays = getAllEssays()
		const tutorials = getAllTutorials()
		const snippets = getAllSnippets()
		const tips = getAllTips()

		essays.forEach(async ({ slug }) => {
			const { meta, code } = await getEssayBySlug(slug)
			const { title, description = '', date, readingTime, authors } = meta
			let data = new URLSearchParams()
			data.append('title', title)
			data.append('description', description)
			data.append('date', JSON.stringify(date))
			data.append('readingTime', JSON.stringify(readingTime))
			data.append('slug', slug)
			data.append('authors', JSON.stringify(authors))

			const options = {
				method: `POST`,
				body: data,
			}

			fetch(`${siteUrl}/api/og-image`, options)
				.then((data) => {
					if (!data.ok) throw data

					console.log(data.body)
				})
				.catch((error) => {
					console.error(error)
				})

			const json = await ky
				.post('api/og-image', {
					prefixUrl: siteUrl,
					json: {
						title: meta.title,
						description: meta.description,
						date: meta.date,
						readingTime: meta.readingTime,
					},
				})
				.json()
			console.log(json)
		})
	} catch (e) {
		console.error(e)
	} finally {
	}
}

I want the image from this API to be downloaded. How do I do it?

Failed to launch browser process

My version is already "next-api-og-image": "^4.3.0". It runs fine locally, but it gives an error on Vercel. I've seen many comments suggesting that downgrading the node version is a good solution. However, Vercel no longer supports Node 14.

[GET] /api/ogImage?name=rotterdame&creator=schwarzskra 17:16:38:27 2022-08-07T14:16:42.986ZERROR Error: Failed to launch the browser process! /tmp/chromium: error while loading shared libraries: libnss3.so: cannot open shared object file: No such file or directory

Query type for defining templates outside the withOGImage

Yeah. Thats probably because of noImplicitAny. By the way there should be query type exported from next-api-og-image since now it is not possible to have function outside withOGImage call scope with types, because inferring does not work and results in error that you had - implicit any.

Originally posted by @neg4n in #7 (comment)

Failed to launch the browser process!

I get this error when I start my next app with yarn dev

error - Error: Failed to launch the browser process! spawn C:\Program Files (x86)\Google\Chrome\Application\chrome.exe ENOENT

I have chrome installed on my system but its, not in the path printed in the error. It is in the 64bit folder. C:\Program Files\Google\Chrome\Application

My ENV

  • Node 16.1.0.0
  • Next 12.1.0
  • chrome-aws-lambda 1.6.3
  • Windows

Change image resolution / page viewport

Hi 👋

I just came across this lib, which is just a better implementation of the one I had 😄
I think it would be useful to have a width and height option, to specify the image resolution, no? Just an idea!

TypeScript example not working

I modified the TypeScript example a little bit like this:

import { withOGImage, NextApiOgImageConfig } from 'next-api-og-image'

interface IHTML {
	heading: NextApiOgImageConfig<'heading'>
}

const html = async ({ heading }: IHTML) => {
	return `
		<html>
			<head>
				<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
			</head>
			<body>
				<h1>${heading}</h1>
			</body>
		</html>
	`
}

const nextApiOgImageConfig = {
	// 'Content-Type' HTTP header
	contentType: 'image/png',
	// 'Cache-Control' HTTP header
	cacheControl: 'max-age 3600, must-revalidate',
	// NOTE: Options within 'dev' object works only when process.env.NODE_ENV === 'development'
	dev: {
		// Whether to replace binary data (image/screenshot) with HTML
		// that can be debugged in Developer Tools
		inspectHtml: true,
	},
	html,
}

export default withOGImage<'heading'>(nextApiOgImageConfig)

And it's not working. I get an error on the last line on nextApiOgImageConfig saying:

Argument of type '{ contentType: string; cacheControl: string; dev: { inspectHtml: boolean; }; html: ({ heading }: IHTML) => Promise<string>; }' is not assignable to parameter of type 'NextApiOgImageConfig<"heading">'.
  Types of property 'html' are incompatible.
    Type '({ heading }: IHTML) => Promise<string>' is not assignable to type '(...queryParams: Record<"heading", string | string[]>[]) => string | Promise<string>'.
      Types of parameters '__0' and 'queryParams' are incompatible.
        Type 'Record<"heading", string | string[]>' is not assignable to type 'IHTML'.
          Types of property 'heading' are incompatible.
            Type 'string | string[]' is not assignable to type 'NextApiOgImageConfig<"heading">'.
              Type 'string' is not assignable to type 'NextApiOgImageConfig<"heading">'.ts(2345)

How do I fix that?

Wait for webfont load?

This thing is amazing.

Is it possible to have it wait for webfonts or should I strictly embed them?

Error: Could not find expected browser (chrome) locally. Run `npm install` to download the correct Chromium revision (901912).

Error: Could not find expected browser (chrome) locally. Run npm install to download the correct Chromium revision (901912).

when build example and start, errors are occurred.
'npm run dev' is ok, but 'npm run build' and 'npm run start' has problem.

reproduce steps

  1. clone this project
  2. npm install
  3. cd example && npm install

after I encountered errors bellow
`It looks like you're trying to use TypeScript but do not have the required package(s) installed.

Please install @types/react by running:`

so i installed @types/react and upgrade next, react
4. npm install --save-dev @types/react && npm install next@latest && npm upgrade react@latest react-dom@latest

  1. npm build && npm start
  2. browser open and go to url http://localhost:3000/api/basic?myQueryParam=asdf

Fix behaviour of checkStrategy()

Currently the checkStrategy() function throws error if no query is provided. This should not happen since its not really an error behavior

Also there seems to be bug in here:

response.json({ message })

because it clearly ignores the errorsInResponse configuration property

[Bug] Could not find expected browser (chrome) locally after `next start`

Environment

next-api-og-image: 2.1.1
next: 11.1.2 / 12.0.4
node: 16.13.0

MacOS 12.0.1 (M1 Chip)
VS Code

Bug Description

This package works fine in dev mode when using next dev. But if I try to run next build & next start I'm getting this error when trying to access the API route:

Error: Could not find expected browser (chrome) locally. Run `npm install` to download the correct Chromium revision (901912).
    at ChromeLauncher.launch (/Users/romanzhuravlov/Documents/GitHub/next-api-og-image/example/node_modules/puppeteer-core/lib/cjs/puppeteer/node/Launcher.js:88:27)
    at async Object.apiResolver (/Users/romanzhuravlov/Documents/GitHub/next-api-og-image/example/node_modules/next/dist/server/api-utils.js:101:9)
    at async Server.handleApiRequest (/Users/romanzhuravlov/Documents/GitHub/next-api-og-image/example/node_modules/next/dist/server/next-server.js:770:9)
    at async Object.fn (/Users/romanzhuravlov/Documents/GitHub/next-api-og-image/example/node_modules/next/dist/server/next-server.js:661:37)
    at async Router.execute (/Users/romanzhuravlov/Documents/GitHub/next-api-og-image/example/node_modules/next/dist/server/router.js:205:32)
    at async Server.run (/Users/romanzhuravlov/Documents/GitHub/next-api-og-image/example/node_modules/next/dist/server/next-server.js:841:29)
    at async Server.handleRequest (/Users/romanzhuravlov/Documents/GitHub/next-api-og-image/example/node_modules/next/dist/server/next-server.js:292:20)

Tried both with my own project and examples from the repo example folder.

npm install doesn't help:

Снимок экрана 2021-11-24 в 16 00 45

Add example with React component

As I mentioned in my previous comment, I've seen many implementations of og-image in Next.js & kept a bookmark. Before this, I was gonna go with https://dominik.sumer.dev/blog/how-to-use-react-to-generate-og-images

It has a section where the author uses a React component. I think a React.js component would be more ideal as we are working with Next.js.

This would also allow for an easier preview with HMR. I am assuming the new dev mode doesn't allow HMR. Lmk if I'm wrong about that :)

Failed to launch browser process

My version is already "next-api-og-image": "^4.2.1", in local running fine but in Vercel gives error

[GET] /api/ogImage?name=rotterdame&creator=schwarzskra
17:16:38:27
2022-08-07T14:16:42.986ZERROR	Error: Failed to launch the browser process!
/tmp/chromium: error while loading shared libraries: libnss3.so: cannot open shared object file: No such file or directory

Error 500 & Content Blocker Issue

Hey there,

I'm using the basic JS example (placed in the Next.JS API route). In both Chrome and Safari, I get a content block error. This only occurs in production (hosted on Vercel), localhost loads fine.

Chrome Error: Failed to load resource: net::ERR_BLOCKED_BY_CLIENT

Safari Error: Content blocker prevented frame displaying ... from loading a resource from https://p.typekit.net/p.css?s=1&...

Code I'm using:

import { withOGImage } from 'next-api-og-image'

export default withOGImage({
  template: {
    react: ({ title }) => <div>🔥 {title}</div>,
  },
})

Dependencies in package.json:

"dependencies": {
    "mongodb": "^4.9.0",
    "next": "12.2.3",
    "next-api-og-image": "^4.3.0",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-markdown": "^8.0.3",
    "react-query": "^3.39.2",
    "socket.io-client": "^4.5.1"
  },
  "devDependencies": {
    "@types/socket.io": "^3.0.2",
    "autoprefixer": "^10.4.8",
    "eslint": "8.21.0",
    "eslint-config-next": "12.2.3",
    "postcss": "^8.4.14",
    "sass": "^1.54.5",
    "tailwindcss": "^3.1.7"
  }

Thank you in advance!

Add example with Tailwind

Awesome work! Love how simple it is. I've checked every existing solution & this feels simplest.

I would love to have something like Bannerbear which creates dynamic previews & I love to style in Tailwind so would love it if I can get a template like Tailwind.

Contributing guidelines

Create contribution guidelines for fellow open sourcerers that want to take part in next-api-og-image development 🔥

Add option to override default chrome executable and arguments

I'm trying to use this package in production, but I have an issue with setting up the environment:

Error: Failed to launch the browser process!
[0914/172601.987555:ERROR:zygote_host_impl_linux.cc(90)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180.
ROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md

Sadly, there's no way to specify chrome executable arguments. It would be nice if there was some option for it. It is also not hard to imagine that someone might need to override default executable (for example if someone runs multiple versions of chrome on same machine), so there could be also option for that.

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.