Giter Site home page Giter Site logo

gridsome-source-storyblok's Introduction

Gridsome Source Storyblok

The official Storyblok integration with Gridsome.

To see it in action take a look at the Storyblok Gridsome Boilerplate.

Version Downloads Stars Status

Install

yarn add gridsome-source-storyblok # or npm install gridsome-source-storyblok

Usage

  1. In gridsome.config.js, declare the use of the plugin and define the options:
// in gridsome.config.js
{
  siteName: 'Gridsome',
  plugins: [
    {
      use: 'gridsome-source-storyblok',
      options: {
        client: {
          accessToken: '<YOUR_ACCESS_TOKEN>'
        },
        types: {
          story: {
            typeName: 'StoryblokEntry'
          }
        }
      }
    }
  ]
}
  1. In the src/templates folder create a .vue file with the same name you defined in the typeName option (default is StoryblokEntry). After that set a <page-query> tag to load the data from GraphQL. For example:
<page-query>
query StoryblokEntry ($id: ID) {
  storyblokEntry (id: $id) {
    id
    slug
    content
  }
}
</page-query>
  1. Edit the file gridsome.server.js to use a GraphQL query to generate the pages using Storyblok's full_slug attribute
module.exports = function (api) {
  api.createPages(async ({ graphql, createPage }) => {
    const { data } = await graphql(`{
      allStoryblokEntry {
        edges {
          node {
            id
            full_slug
          }
        }
      }
    }`)

    data.allStoryblokEntry.edges.forEach(({ node }) => {
      createPage({
        path: `/${node.full_slug}`,
        component: './src/templates/StoryblokEntry.vue',
        context: {
          id: node.id
        }
      })
    })
  })
}

The options object in details

When you declare the use of the Storyblok plugin you can pass following options:

{
  use: 'gridsome-source-storyblok',
  options: {
    client: {
      // The Storyblok JS Client options here (https://github.com/storyblok/storyblok-js-client)
      accessToken: '<YOUR_ACCESS_TOKEN>' // required!
    },
    version: 'draft', // Optional. Can be draft or published (default draft)
    // Optional: Config story and tag types names and request calls
    types: {
      story: {
        name: 'StoryblokEntry', // The name of Story template and type (default StoryblokEntry)
        params: {} // Additional query parameters
      },
      tag: {
        name: 'StoryblokTag', // The name of Tag template and type (default StoryblokTag)
        params: {} // Additional query parameters
      }
    },
    downloadImages: true, // Optional. default false,
    imageDirectory: 'storyblok_images', // Optional. Folder to put the downloaded images
    // Optional: Get additional types like datasources, links or datasource_entries
    additionalTypes: [
      {
        type: 'datasources', // required
        name: 'StoryblokDatasource' // required
      },
      {
        type: 'datasource_entries',
        name: 'StoryblokDatasourceEntry',
        params: { ...additionalQueryParams } // optional
      },
      {
        type: 'links',
        name: 'StoryblokLink'
      }
    ]
  }
}

Rendering of the Rich Text field

This plugin comes with a built in renderer to get html output from Storyblok's Rich Text field. Create and register a Richtext.vue component with the code below to use the renderer in your components like this: <richtext :text="blok.richtext"></richtext>.

<template>
  <div>
    <div v-html="richtext"></div>
  </div>
</template>

<script>
export default {
  props: ['text'],
  computed: {
    richtext() {
      return this.text ? this.$storyapi.richTextResolver.render(this.text) : ''
    }
  }
}
</script>

Downloading images

When downloadImages option is marked as true, this plugin will be searching in each story for a image and download it to src/<imageDirectory> folder. In your components, you can use the g-image tag. An important thing is that image property in story will be a object with some fields, not a string. Bellow, we show you an example of this:

<template>
  <div>
    <g-image :src="imageURL"></g-image>
  </div>
</template>

<script>
export default {
  props: ['blok'],
  computed: {
    imageURL () {
      // When options.downloadImages is false, the image property is a string
      if (typeof this.blok.image === 'string') {
        return this.blok.image
      }

      // When options.downloadImages is true, the image property is a object
      // Reference of this: https://github.com/gridsome/gridsome/issues/292
      const path = this.blok.image.path
      return require('!!assets-loader?width=800&quality=100&fit=inside!~/' + path)
    }
  }
}
</script>

<style scoped>
img {
  max-width: 100%;
}
</style>

Working with Tags

By default, this plugin will get all tags and create a reference to stories entries (as described in create-schema function), so it's possible to list stories from tag, for example.

You can change the name of template file and types by setting the options.types.tag.name option in gridsome.config.js (StoryblokTag is default).

Example

Create a StoryblokTag.vue file in src/templates folder with the following code:

<template>
  <Layout>
    <h1>{{ $page.storyblokTag.name }}</h1>
    <ul>
      <li v-for="edge in $page.storyblokTag.belongsTo.edges" :key="edge.node.id">
        <g-link :to="edge.node.full_slug">
          {{ edge.node.name }}
        </g-link>
      </li>
    </ul>
  </Layout>
</template>

<page-query>
query ($id: ID!) {
  storyblokTag(id: $id) {
    name
    belongsTo {
      edges {
        node {
          ... on StoryblokEntry {
            id
            full_slug
            name
          }
        }
      }
    }
  }
}
</page-query>

In your gridsome.server.js file, it will be necessary to create a pages for each tag as the following:

module.exports = function (api) {
  api.createPages(async ({ graphql, createPage }) => {
    // previous code (create pages to stories)

    const { data: tagData } = await graphql(`{
      allStoryblokTag {
        edges {
          node {
            id
            name
          }
        }
      }
    }`)

    tagData.allStoryblokTag.edges.forEach(({ node }) => {
      createPage({
        path: `/tag/${node.name}`,
        component: './src/templates/StoryblokTag.vue',
        context: {
          id: node.id
        }
      })
    })
  })
})

That's all! In your browser you can view a list of stories by the foo tag in http://localhost:8080/tag/foo.

Load data to different collections

To load data to multiple collections, you need to declare the configuration multiple times in gridsome.config.js. Like this:

{
  siteName: 'Gridsome',
  plugins: [
    // default collection
    {
      use: 'gridsome-source-storyblok',
      options: {
        client: {
          accessToken: '<YOUR_ACCESS_TOKEN>'
        }
      }
    },

    // specific collection (blogging for example)
    {
      use: 'gridsome-source-storyblok',
      options: {
        client: {
          accessToken: '<YOUR_ACCESS_TOKEN>'
        },
        types: {
          story: {
            name: 'StoryblokBlogEntry',
            params: {
              starts_with: 'blog/'
            }
          },
          tag: {
            typeName: 'StoryblokBlogTag'
          }
        }
      }
    }
  ]
}

And, in your gridsome.server.js, you can generate your pages for each collection, attending to the name given to each collection.

Get Space informations

It is possible to get the space informations using the GraphQL Data Layer. The space information will be storage in Gridsome's global metadata, so, it will be avaialable for your entire project.

To get the space informations, you can set this following query in your <static-query> in your vue component:

query {
  metadata {
    storyblokSpace {
      id
      name
      version
      language_codes
    }
  }
}

Contribution

Fork me on Github.

This project use semantic-release for generate new versions by using commit messages and we use the Angular Convention to naming the commits. Check this question about it in semantic-release FAQ.

gridsome-source-storyblok's People

Contributors

emanuelgsouza avatar onefriendaday avatar samuells avatar vinicius73 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

Watchers

 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

gridsome-source-storyblok's Issues

[Question] How to load a story in a static query by its path?

I have several stories in a content folder named "Shared" - each contains data that is loaded by static queries in generic components, such as the navigation bar and footer. I want to load that data using a static query, but using the story's ID as an argument is not workable since this project needs to be compatible with more than one space.

<static-query>
query StoryblokEntry {
  storyblokEntry(path: "shared/navbar") {
    content
  }
}
</static-query>

Nothing is returned by the above query, though "path" is a valid argument.

I have also created a second config specifically for the Navbar story:

    {
      use: 'gridsome-source-storyblok',
      options: {
        client: {
          accessToken: process.env.GRIDSOME_STORYBLOK_API_KEY
        },
        params: {
          starts_with: 'shared/navbar'
        },
        version: 'draft',
        typeName: 'NavbarEntry'
      }
    },

With the above, the following still returns nothing:

<static-query>
query NavbarEntry {
  navbarEntry {
    content
  }
}
</static-query>

How to use setComponentResolver?

Where would be the best part to add "setComponentResolver" for the Rich Text rendering?

I've tried adding it before I need it, but it doesn't seem to get picked up:

this.$storyapi.setComponentResolver((component, blok) => {
    return `<component :blok='${JSON.stringify(blok)}'
                 is="${component}"></component>`
  })

Retrieving entries for a single datasource returns entries for all datasources

Hi, using this code to get everything in the authors datasource always returns every entry
from all my datasources.

The query is:

query {
  allAuthorEntries {
    edges {
      node {
        id
        name
        value
      }
    }
  }
}

and the options settings (that I hoped would limit it to only authors) is
set in gridsome.config.js

module.exports = {
    siteName: 'Invativa 2 Gridsome Storyblok',
    plugins: [
        {
            use: 'gridsome-source-storyblok',
            options: {
                client: {
                    accessToken: '<myVeryOwnToken>'
                },
                version: 'published',
                typeName: 'StoryblokEntry',
                additionalTypes: [
                    {
                         type: 'datasource_entries', 
                         name: 'AuthorEntries', 
                         params: {"datasource": "authors"}}
                ]
            }
        }
    ]
}

Anyone having any ideas what I'm doing wrong here?

The automated release is failing 🚨

🚨 The automated release from the master branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can resolve this πŸ’ͺ.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here is some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


Invalid npm token.

The npm token configured in the NPM_TOKEN environment variable must be a valid token allowing to publish to the registry https://registry.npmjs.org/.

If you are using Two-Factor Authentication, make configure the auth-only level is supported. semantic-release cannot publish with the default auth-and-writes level.

Please make sure to set the NPM_TOKEN environment variable in your CI with the exact value of the npm token.


Good luck with your project ✨

Your semantic-release bot πŸ“¦πŸš€

[Question] Noob question: How to eager load?

I opened up this question in StackOverflow, but couldn't get an answer yet. Maybe I have here more luck.

My background is coming from normal REST-APIs and now I'm a bit overwhelmed.

I'm trying to fetch with one graphql query all needed information for a blog post.

The first query loads with the ID everything from a specific blog post but is not loading nested data like categories or authors.

For example:

<page-query>
    query StoryblokEntry ($id: ID) {
        storyblokEntry (id: $id) {
            id
            slug
            content
        }
    }
</page-query>

When I test the query with the ID of "story-3850216-default", I get this result:

{
  "data": {
    "storyblokEntry": {
      "id": "story-3850216-default",
      "slug": "hello-world",
      "content": {
        "_uid": "f5a0bd9d-ec13-4381-ae5f-a7e2bb093fbe",
        "title": "Hello World πŸ‘‹",
        "authors": [
          "61dec800-1b17-443e-961a-1cf8ef08d803"
        ],
        "content": "hi",
        "component": "article-post",
        "categories": [
          "e1509e6e-2007-4d4b-8857-4bc1105c5505"
        ],
      }
    }
  }
}

What should I do, so that the values of authors and categories are eager loaded?

Edit:

Or better how could I implement this kinda of query?

{
  ArticlepostItems {
    items {
      id
      created_at
      content {
        component
        content
        title
        authors {
          content
        }
        categories {
          content
        }
      }
    }
  }
}

Or do I have a thinking error? Do I even need this plugin or is it just to create components dynamically, but is not for nested data?

With 1.0.2 downloaded image properties change

After upgrading to 1.0.2 my image properties seem to change when in config downloadImages: true

Now

{
   id:1187705,
   alt:null,
   name:"",
   focus:null,
   title:null,
   url:"https://a.storyblok.com/f/837397/1200x630/a97e2e6a90/filename.png",
   filename:"filename.png",
   path:"cms_images/filename.png"
   copyright:null,
   fieldtype:"asset"
}

becomes

{
   id:1187705,
   alt:null,
   name:"",
   focus:null,
   title:null,
   filename:{
      url:"https://a.storyblok.com/f/837397/1200x630/a97e2e6a90/filename.png",
      filename:"filename.png",
      path:"cms_images/filename.png"
   },
   copyright:null,
   fieldtype:"asset"
}

Is this intentional? I would prefer being able to have the same code to extract a filename and path no matter is the downloadImages is true

I'm not sure, but could it have something to do with reassignment of variable 'value' in this new code?
910eefc#diff-8d6b1f9270334e293ad1f2ddec84e050

Adding additional query parameter (argument) uuid

Hi there! Would you be so kind and explain to me how I can add an additional query parameter to my graphql query? Specifically I do need the uuid (not the id) to fetch my stories. This is the only parameter the link object in Storyblok would give me.

This is what I am getting back from the Storyblok Delivery API:

"blog_select": [
"2d3c12d8-8f8b-4a40-97cd-xxxxxxxxxxx",
"ce5e8052-476b-4eb6-9d2b-xxxxxxxxxxx",
"2c12c204-1837-4913-b2ff-xxxxxxxxxx"
]

And I want to use those uuid's to fetch the respective Stories. So my static-query can then look like this:

{
  storyblokEntry(uuid: 2d3c12d8-8f8b-4a40-97cd-xxxxxxxxxxx) {
    content
  }
}

Thanks for your help!

Download images

Images will not download when image extension is in uppercase like .JPG or .PNG when uploaded to storyblok

NPM security warning - storyblok-js-client > axios

Getting a NPM audit security warning on installation of gridsome-source-storyblok

              === npm audit security report ===


                             Manual Review
         Some vulnerabilities require your attention to resolve

      Visit https://go.npm.me/audit-guide for additional guidance

High Server-Side Request Forgery

Package axios

Patched in >=0.21.1

Dependency of gridsome-source-storyblok

Path gridsome-source-storyblok > storyblok-js-client > axios

More info https://npmjs.com/advisories/1594

loading multiple data into graphql issue

I'm not sure what I'm doing wrong, I've followed the way to do it from issue #9

here is what I've done:

//gridsome.config.js

module.exports = {
  siteName: 'Gridsome',
  plugins: [
    {
      use: 'gridsome-source-storyblok',
      options: {
        client: {
          accessToken: API_KEY
        },
        version: 'draft',
        typeName: 'StoryblokEntry'
      }
    },
    {
      use: 'gridsome-source-storyblok',
      options: {
        client: {
          accessToken: API_KEY
        },
        params: {
          starts_with: 'products/'
        },
        typeName: 'ProductEntry'
      }
    }
  ]
}
//gridsome.server.js

module.exports = function (api) {
  api.loadSource(({ addCollection }) => {
    // Use the Data Store API here: https://gridsome.org/docs/data-store-api/
  })

  api.createPages(async ({ graphql, createPage }) => {
    const { data } = await graphql(`{
      allProductEntry {
        edges {
          node {
            id
            full_slug
          }
        }
      }
    }`)


    data.allProductEntry.edges.forEach(({ node }) => {
      createPage({
        path: `/${node.full_slug}`,
        component: './src/templates/ProductEntry.vue',
        context: {
          id: node.id
        }
      })
    })
  })
}

I'm getting the following issue when building:

TypeError: gridsome.server.js: Cannot read property 'allProductEntry' of undefined (26:10)

  24 |
  25 |
> 26 |     data.allProductEntry.edges.forEach(({ node }) => {
     |          ^
  27 |       createPage({
  28 |         path: `/${node.full_slug}`,
  29 |         component: './src/templates/ProductEntry.vue',

It seems like the Graphql query of "allProductEntry" is not running, leaving "data" undefined

any help would be much appreciated

How can I store data in two different collections?

[Question] Is there a way to load multiple collection into the Gridsome GraphQL Layer? Let's say I have a common corporate site with some sales pages and a Blog section. The blog pages are stored within a blog-Folder in the Storyblok CMS - so the path will be my-website.com/blog/...

Is it possible to story all the data in a StoryblokEntry collection but all blog posts in a BlogEntry collection? That way, within Gridsome, I could assign two different templates to those collections.

Thanks for your help.

Cannot find module 'core-js/modules/es6.regexp.split'

Hi, I keep getting the Cannot find module 'core-js/modules/es6.regexp.split' error when trying to use this plugin.

> gridsome develop

Gridsome v0.7.13

Initializing plugins...
Error: Cannot find module 'core-js/modules/es6.regexp.split'
Require stack:
- C:\Users\jonat\Desktop\Projects\Code Projects\Websites\rosemary-hale-gridsome\node_modules\storyblok-js-client\dist\index.js
- C:\Users\jonat\Desktop\Projects\Code Projects\Websites\rosemary-hale-gridsome\node_modules\gridsome-source-storyblok\index.js
- C:\Users\jonat\Desktop\Projects\Code Projects\Websites\rosemary-hale-gridsome\node_modules\gridsome\lib\app\Plugins.js
- C:\Users\jonat\Desktop\Projects\Code Projects\Websites\rosemary-hale-gridsome\node_modules\gridsome\lib\app\App.js
- C:\Users\jonat\Desktop\Projects\Code Projects\Websites\rosemary-hale-gridsome\node_modules\gridsome\lib\app\index.js
- C:\Users\jonat\Desktop\Projects\Code Projects\Websites\rosemary-hale-gridsome\node_modules\gridsome\lib\develop.js
- C:\Users\jonat\Desktop\Projects\Code Projects\Websites\rosemary-hale-gridsome\node_modules\gridsome\index.js
- C:\Users\jonat\Desktop\Projects\Code Projects\Websites\rosemary-hale-gridsome\node_modules\gridsome\node_modules\@gridsome\cli\bin\gridsome.js      
- C:\Users\jonat\Desktop\Projects\Code Projects\Websites\rosemary-hale-gridsome\node_modules\gridsome\bin\gridsome.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:982:15)
    at Function.Module._load (internal/modules/cjs/loader.js:864:27)
    at Module.require (internal/modules/cjs/loader.js:1044:19)
    at require (internal/modules/cjs/helpers.js:77:18)
    at Object.<anonymous> (C:\Users\jonat\Desktop\Projects\Code Projects\Websites\rosemary-hale-gridsome\node_modules\storyblok-js-client\dist\index.js:15:1)
    at Module._compile (internal/modules/cjs/loader.js:1158:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
    at Module.load (internal/modules/cjs/loader.js:1002:32)
    at Function.Module._load (internal/modules/cjs/loader.js:901:14)
    at Module.require (internal/modules/cjs/loader.js:1044:19)
    at require (internal/modules/cjs/helpers.js:77:18)
    at Object.<anonymous> (C:\Users\jonat\Desktop\Projects\Code Projects\Websites\rosemary-hale-gridsome\node_modules\gridsome-source-storyblok\index.js:1:25)
    at Module._compile (internal/modules/cjs/loader.js:1158:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
    at Module.load (internal/modules/cjs/loader.js:1002:32)
    at Function.Module._load (internal/modules/cjs/loader.js:901:14)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! rosemary-hale-gridsome@ develop: `gridsome develop`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the rosemary-hale-gridsome@ develop script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

[Question] Is it possible to run queries on data inside the content JSONObject?

We use this plugin with gridsome and storyblok and it works great.

However, when fetching articles, I would sometimes like to filter on some value inside the content field.
Currently I cannot figure out if this is even possible. Also just selecting a few fields inside the content field would be nice.

Is it possible to specify a schema for some types manually? or have them be autogenerated?

As there is no schema right now I get
"message": "Field "content" must not have a selection since type "JSONObject" has no subfields.",
back in the playground.

Any ideas?

Webpack bundling Gridsome components even when unused on Storyblok page

I have a Gridsome website which pulls content in from Storyblok using gridsome-source-storyblok.

The site has about 50 possible components that can be added from within Storyblok in order to build the page.

The components are registered in the main template and should only be loaded if they are getting used, however it seems that Webpack is bundling all of the Gridsome components, even when they're not being used on the Storyblok page.

This is my function for loading the components:

// StoryblokEntry.vue
  mounted() {
    // ...
    for(let component of components) {
      this.$options.components[`${component}`] = require(`../components/${component}`).default
    }
    // ...
  }

This is my Webpack configuration:

// gridsome.config.js
module.exports = {
  // ...
  plugins: [
  // ...
  chainWebpack(config) {
    config.resolve.alias.set('@storyblok', '@/media-storyblok')
    config.mode('production')
    // Load variables for all vue-files
    const types = ['vue-modules', 'vue', 'normal-modules', 'normal']
  }
}

Lighthouse is returning very low scores of just 25/100 for Performance with the suggestion to Remove unused JavaScript to save 5.85s of load time. The fix for this is clearly for Webpack to only bundle the Gridsome components when they're being used on the Storyblok page, but this does not seem possible(?).

image

I notice there is an unanswered StackOverflow question that seems like it could concern the same issues https://stackoverflow.com/questions/57972231/gridsome-vue-js-how-do-i-reduce-the-core-bundle-size

Download images -> Doesn't work with two or more images with the same filename

When setting the download images to true, the files do not get unique names. Is this by design?

If I somewhere in a component upload an image with the filename "1.jpg" (or or "hero.jpg" or whatever) and somewhere else upload a different image, but with the same name and extension: then when downloading, it says: [gridsome-source-storyblok] Image 1.jpg already downloaded.

I don't think it is an entirely unrealistic example to have some images with the same name.
Is this something that will be fixed?

[Question] How to create pages to list stories by tag?

[Question] I would like to have pages for listing stories which have the same tags. Is it possible and how to do it?

The solution is probably to use api.createPages in gridsome.server.js but i'm a bit confused and i don't know how to use it well

downloadImage not working

Hi, I'm trying to use the downloadImage option, but I can't get it working.

      use: 'gridsome-source-storyblok',
      options: {
        client: {
          accessToken: ***
        },
        downloadImages: true,
        imageDirectory: 'storyblok_images',
        params: {
          resolve_links: 'story',
          resolve_relations: 'navigation, preview-articles.articles, page-map.organisations,partners.partners,partners.poweredby,existing_element.element'
        },
      }
    }

And in my components i have code like

<g-image :src="image.filename" :alt="image.name" width="500"/>

Am i doing something wrong?

Adding 2 different document in Gridsome

I would like to create API document as well as user guide in gridsome. How to configure gridsome.config.js file for that. whenever I am trying I am getting same side bar which should be different for 2 different document.

Is ` resolveRelations` supported?

Hello,

Is there an option for setting resolveRelations similar to gatsby-source-storyblok. For example, I would like to resolve post.author to an Object. Currently, I am getting a String id.

Thanks for the assistance πŸ™

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.