Git Product home page Git Product logo

example-auth's Introduction

ReactQL v4.3.0

license Twitter Follow Deploy to Netlify

Universal front-end React + GraphQL starter kit, written in Typescript.

https://reactql.org

Features

Front-end stack

Server-side rendering

  • Built-in Koa 2 web server, with async/await routing.
  • Full route-aware server-side rendering (SSR) of initial HTML.
  • Universal building - both browser + Node.js web server compile down to static files, for fast server re-spawning.
  • Per-request GraphQL store. Store state is dehydrated via SSR, and rehydrated automatically on the client.
  • MobX for app-wide flux/store state, for automatically re-rendering any React component that 'listens' to state. Fully typed state!
  • Full page React via built-in SSR component - every byte of your HTML is React.
  • SSR in both development and production, even with hot-code reload.

Real-time

  • Hot code reloading; zero refresh, real-time updates in development.
  • Development web server that automatically sends patches on code changes, and restarts the built-in Web server for SSR renders that reflect what you'd see in production.
  • WebSocket subscription query support for real-time data (just set WS_SUBSCRIPTIONS=1 in .env)

Code optimisation

  • Webpack v4, with tree shaking -- dead code paths are automatically eliminated.
  • Asynchronous code loading when import()'ing inside a function.
  • Automatic per-vendor chunk splitting/hashing, for aggressive caching (especially good behind a HTTP/2 proxy!)
  • Gzip/Brotli minification of static assets.
  • CSS code is combined, minified and optimised automatically - even if you use SASS, LESS and CSS together!

Styles

  • Emotion, for writing CSS styles inline and generating the minimal CSS required to properly render your components.
  • PostCSS v7 with next-gen CSS and automatic vendor prefixing when importing .css, .scss or .less files.
  • SASS and LESS support (also parsed through PostCSS.)
  • Automatic vendor prefixing - write modern CSS, and let the compiler take care of browser compatibility.
  • Mix and match SASS, LESS and regular CSS - without conflicts!
  • CSS modules - your classes are hashed automatically, to avoid namespace conflicts.
  • Compatible with Foundation, Bootstrap, Material UI and more. Simply configure via a .global.(css|scss|less) import to preserve class names.

Production-ready

  • Production bundling via npm run production, that generates optimised server and client code.
  • Static compression using the Gzip and Brotli algorithms for the serving of static assets as pre-compressed .gz and .br files (your entire app's main.js.bz - including all dependencies - goes from 346kb -> 89kb!)
  • Static bundling via npm run build:static. Don't need server-side rendering? No problem. Easily deploy a client-only SPA to any static web host (Netlify, etc.)

Developer support

  • Written in Typescript with full type support, out the box (all external @types/* packages installed)
  • Heavily documented code

Quick start

Grab and unpack the latest version, install all dependencies, and start a server:

wget -qO- https://github.com/leebenson/reactql/archive/4.5.1.tar.gz | tar xvz
cd reactql-4.5.1
npm i
npm start

Your development server is now running on http://localhost:3000

Building GraphQL HOCs

By default, your GraphQL schema lives in schema/schema.graphql

To create fully Typescript-typed Apollo React HOCs based on your schema, simply put the query in a .graphql anywhere inside the source folder, and run:

npm run gen:graphql

You can then import the query like we do in the HackerNews demo component:

// Query to get top stories from HackerNews
import { GetHackerNewsTopStoriesComponent } from "@/graphql";

And use it as follows:

<GetHackerNewsTopStoriesComponent>
    {({ data, loading, error }) => (...)}
</GetHackerNewsTopStoriesComponent>

To get access to the underlying gql-templated query (in case you need it for refetching, etc), in this case it'd be GetHackerNewsTopStoriesDocument.

See GraphQL Code Generator for more details on how it works.

You can also edit codegen.yml in the root to point to a remote schema, or change the file location.

Development mode

Development mode offers a few useful features:

  • Hot code reloading. Make a change anywhere in your code base (outside of the Webpack config), and changes will be pushed down the browser automatically - without page reloads. This happens for React, Emotion, SASS - pretty much anything.

  • Full source maps for Javascript and CSS.

  • Full server-side rendering, with automatic Koa web server restarting on code changes. This ensures the initial HTML render will always reflect your latest code changes.

To get started, simply run:

npm start

A server will be started on http://localhost:3000

Production mode

In production mode, the following happens:

  • All assets are optimised and minified. Javascript, CSS, images, are all compiled down to static files that will appear in dist.

  • Assets are also compressed into .gz (Gzip) and .br (Brotli) versions, which are served automatically to all capable browsers.

  • If files have been generated in a previous run, they will be re-used on subsequent runs. This ensures really fast server start-up times after the initial build.

To build and run for production, use:

npm run production

Files will be generated in ./dist, and a server will also be spawned at http://localhost:3000

Clean the cached production build with npm run clean, or run npm run clean-production to both clean and re-run the production build, as needed.

Build mode

If you only want to build assets and not actually run the server, use:

npm run build:production

This is used in the Dockerfile, for example, to pre-compile assets and ensure faster start-up times when spawning a new container.

Static bundling for client-only SPAs

If you're targeting a client-only SPA and hosting statically, you probably don't want to run a Koa web server to handle HTTP requests and render React.

Instead, you can use static mode, which produces the client-side JS, CSS and assets files, along with an index.html for minimal bootstrapping, and dumps them in dist/public.

You can then upload the contents of that folder wherever you like - et voila, you'll have a working client-side Single Page App!

There are two static modes available -- for dev and production:

### Development (hot-code reload)

Just like the full-stack version, dev mode gives you hot code reloading, so changes to your local files will be pushed to the browser.

To activate static dev mode, just run:

npm run dev:static

Your client-side SPA will be online at http://localhost:3000, just like normal.

### Production (static deployment)

To build your client-side files ready for production deployment, run:

npm run build:static

You'll get everything in that 'regular' building provides you with plus a index.html to bootstrap your JS, just without the server parts.

Modifying the index.html template

If you want to make changes to the index.html file that's used for static bundling, edit src/views/static.html

NPM commands

Here's a list of all the NPM script commands available out-the-box:

Command What it does
npm run build:production Builds production-ready client/server bundles, but doesn't start a server.
npm run build:static Builds production-ready client bundle and index.html; ignores server bundling.
npm run clean Removes the dist folder, and any previously built client/server bundle.
npm run dev Runs a univeral dev server in memory; auto restarts on code changes and uses hot-code reload in the browser. Does not output anything to dist.
npm run dev:static Runs a client-only dev server using [src/views/static.html] as the template; full hot-code reload. Also doesn't output anything to dist.
npm run production Builds and runs a production-ready client/server bundle. If previously built, will re-use cached files automatically (run npm run clean to clear cache.)
npm run production:clean Same as above, but cleans dist first to ensure a fresh re-build.
npm start Shortcut for npm run dev.

Project layout

The important stuff is in src.

Here's a quick run-through of each sub-folder and what you'll find in it:

  • src/components - React components. Follow the import flow at root.tsx to figure out the component render chain and routing. I've included an example component that shows off some Apollo GraphQL and MobX features, including incrementing a local counter and pulling top news stories from Hacker News (a live GraphQL server endpoint.)

  • src/entry - The client and server entry points, which call on src/components/root.tsx to isomorphically render the React chain in both environments.

  • src/global - A good place for anything that's used through your entire app, like global styles. I've started you off with a styles.ts that sets globally inlined Emotion CSS, as well as pulls in a global .scss file -- to show you how both types of CSS work.

  • src/lib - Internal libraries/helpers. There's an apollo.ts which builds a universal Apollo Client. Plus, Koa middleware to handle hot-code reloading in development and some other Webpack helpers.

  • src/queries - Your GraphQL queries. There's just one by default - for pulling the top stories from Hacker News to display in the example component.

  • src/runner - Development and production runners that spawn the Webpack build process in each environment.

  • src/views - View components that fall outside of the usual React component chain, for use on the server. In here, ssr.tsx takes care of rendering the root HTML that's sent down the wire to the client. Note this is also a React component - your whole app will render as React! - and static.html serves as a template for rendering a client-side SPA. Update it as needed.

  • src/webpack - The Webpack 4 configuration files that do the heavy lifting to transform our Typescript code, images and CSS into optimised and minified assets that wind up in the dist folder at the root. Handles both the client and server environments.

You'll also find some other useful goodies in the root...

  • .env - Change your GRAPHQL server endpoint, WS_SUBSCRIPTIONS=1 for built-in WebSocket support, HOST if you want to bind the server to something other than localhost, and LOCAL_STORAGE_KEY to set the root key for saving MobX state locally in the client for automatic re-loading in a later session.

  • .nvmrc - Specify your preferred Node.js version, for use with NVM and used by many continuous deployment tools. Defaults to v12.2.0

  • codegen.yml - Settings for GraphQL Code Generator (which you can run with npm run gen:graphql to generate types/HOCs based on your GraphQL queries/mutations.)

  • netlify.toml - Build instructions for fast Netlify deployments. Tip: To quickly deploy a demo ReactQL app, click here.

  • types - Some basic types that allow you to import fonts, images, CSS/SASS/LESS files, and allow use of the global SERVER boolean and GRAPHQL endpoint data in your IDE.

  • Typescript configuration via tsconfig.json

  • A sample multi-build Dockerfile based on Node 11.8 and Alpine, for quickly deploying your code base to production.

Follow @reactql for updates

Get the latest updates by following us on Twitter: https://twitter.com/reactql

Twitter Follow

New to GraphQL? Need help?

ReactQL

Join the ReactQL slack channel here.

Watch my free 45 minute YouTube video, for a live coding walk-through of putting together a GraphQL server with a database. Learn how to write queries, mutations and handle nested/related data.

Hire me

I'm a full-stack developer with 20+ years experience. As well as 9 years hands-on dev with Node.js, I'm fluent in Python, Go, SQL and NoSQL. I specialise in building robust, scalable products from scratch, and helping you deploy fast.

If you're looking for a senior developer who can help you get your product out the door quickly, reach me at [email protected]. I'm occasionally available to take on remote contracts when I'm not working on my own projects.

example-auth's People

Contributors

leebenson 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

Watchers

 avatar  avatar  avatar

example-auth's Issues

heroku build error

I ran a npm build-run but memory on heroku exceded so i needed to build on local machine. Now i only execute npm run server on heroku but every time i get this error
Process exited with status 143
Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch
screenshot from 2017-09-15 14-48-45
I am not very profficient with heroku, maybe i dont know how to set port. When i console log from server.js process.env.PORT i get 4000. I red that heroku dynamically assigns port. Help me please :D
Thanks in advance!

Populating createdBy / author field?

Imagine you're persisting a new object to the database using a mutation (in this example it's a generic 'product') and you want to set a createdBy / author field to that of the current user. What is the recommended way of doing this?

For instance, I imagine you can use context to store current user id and then grab it in the GraphQL mutation (as below) but I guess this would be bad from a security perspective(?):

export const createProductMutation = {
  type: ProductResponseType,
  args: {
    title: {
      type: GraphQLString,
    },
    content: {
      type: GraphQLString,
    },
    createdBy: {
      type: GraphQLString,
    },
  },
  async resolve(_, args, ctx) {
    try {
      const product = await createProduct({ ...args, createdBy: ctx.state.[CURRENT_USER_ID] });
      return {
        ok: true,
        product,
      };
    } catch (e) {
      return {
        ok: false,
        errors: e,
      };
    }
  },
};

But, I guess you can also do this through GraphQL itself e.g. by setting the createdBy field on the ProductType to SessionType and access the current user that way?

In addition, with Sequelize there's the getUser() function that comes built-in with associations.

So, I guess I'm just looking for a bit of guidance on what is the best route to go down here?

Missing ROOT_QUERY in Apollo Cache Proxy

Hi,

First of all, thanks for this great library!
I'm trying to replicate this authentication example with mongodb, and I'm almost there, but there's one little thing I can't get a grasp on.

Just to clarify, your code works fine. The problem is that I have no idea why.

See, in your loginMutation in session.js file, the login mutation has SessionType type, but it's returning an object with ok and session. Why?
SessionType doesn't have the session field, so shouldn't it be:

return {
   	ok: true,
	...session,
}

Another thing is, for some reason, in the login.js in the update function, my proxy object doesn't have the ROOT_QUERY field after running the login mutation for some reason which crashes the proxy.readQuery() function.

In my login mutation, I've changed things a little and removed ok and errors fields as I'm relying on exceptions to provide errors and I've change the code to return session on its own rather than wrapping it in an object. Could that be an issue?

Here's my login mutation code:


const SessionType = new GraphQLObjectType({
  name: 'Session',
  description: 'User login session',
  fields: () => ({
    jwt: {
      type: GraphQLString,
      resolve(obj) {
        let session = obj.session !== undefined ? obj.session : obj;
        return session.getJWT();
      },
    },
    user: {
      type: UserType,
      async resolve(obj) {
        const cursor = User.findById(obj.userId).cursor();
        const user = await cursor.next();
        return user;
      },
    },
  }),
});

const SessionMutations = {
  login: {
    type: SessionType,
    args: {
      email: {
        type: GraphQLString,
      },
      password: {
        type: GraphQLString,
      },
    },
    async resolve(_, args, ctx) {
      const session = await login(args);
      // If getting the JWT didn't throw, then we know we have a valid
      // JWT -- store it on a cookie so that we can re-use it for future
      // requests to the server
      ctx.cookies.set(config.cookieName, session.getJWT(), {
        expires: session.expires,
      });

      // Return the session record from the DB
      return session;
    },
  },
};

export {
  SessionType,
  SessionMutations,
};

I'm deliberately not wrapping the login function in try{} so the exception is catched by Apollo rather than sending errors in SessionType.

Also, here are the screenshots of what's in the CacheProxy on yours and my projects:

Yours:
screen shot 2018-01-03 at 23 10 58

Mine:
screen shot 2018-01-03 at 23 31 37

And because the ROOT_QUERY is missing, it throws the Can't find field session on object (ROOT_QUERY) undefined. error.

Any ideas?
Thanks again
Konrad

Pass cookies to external graphql on ssr

Hello, Lee!
Thank you vey mutch for your great example!

My question a little bit outside. What to do if i use external graphQL endpoint? How to send cookie with JWT in case of server rendering?
I recieve it in koa, but can't understand how to pass cookie to graphQL request.

build-static broken

I am exploring reactql and try to run build-static, but it doesn't work. It says it can't find static.js.

Log:

$ npm run build-static

> @2.9.5 build-static /Users/briandombrowski/Work/Development/ReactQL/example-base
> cross-env NODE_ENV=production WEBPACK_CONFIG=static webpack --colors

Error: static.js not found or has errors:
{ Error: Cannot find module '/Users/briandombrowski/Work/Development/ReactQL/example-base/node_modules/node-zopfli/lib/binding/node-v51-darwin-x64/zopfli.node'
    at Function.Module._resolveFilename (module.js:472:15)
    at Function.Module._load (module.js:420:25)
    at Module.require (module.js:500:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/Users/briandombrowski/Work/Development/ReactQL/example-base/node_modules/node-zopfli/lib/zopfli.js:7:14)
    at Module._compile (module.js:573:32)
    at Module._extensions..js (module.js:582:10)
    at Object.require.extensions.(anonymous function) [as .js] (/Users/briandombrowski/Work/Development/ReactQL/example-base/node_modules/babel-register/lib/node.js:152:7)
    at Module.load (module.js:490:32)
    at tryModuleLoad (module.js:449:12)
    at Function.Module._load (module.js:441:3)
    at Module.require (module.js:500:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/Users/briandombrowski/Work/Development/ReactQL/example-base/node_modules/zopfli-webpack-plugin/dist/index.js:17:19)
    at Module._compile (module.js:573:32)
    at Module._extensions..js (module.js:582:10)
    at Object.require.extensions.(anonymous function) [as .js] (/Users/briandombrowski/Work/Development/ReactQL/example-base/node_modules/babel-register/lib/node.js:152:7) code: 'MODULE_NOT_FOUND' }

Replace isemail by validator

Since validator.js is required for sequelize, why not add it as a project dependency and use it for email validation?

how to handle unauthorized users

Hi Lee,

Was hoping to see your approach to handling/routing unauthorized users. For example:

  • redirecting all (or a subset of) URLs to /login if they aren't already logged in
  • making particular components require authentication (like an admin top bar)
  • redirecting to /login on token expiry
  • etc...

Any plans to include any of this in the example?

Can't get user

I'm new using reactql, but wanna try it for my work.
obj.session.jwt() is done, but I cant get obj.session.getUser().

TypeError: obj.session.getUser is not a function
    at user (/Users/nwicakson/Documents/TA/informatics-reactql/dist/server_dev.js:3702:43)
    at resolveFieldValueOrError (/Users/nwicakson/Documents/TA/informatics-reactql/node_modules/graphql/execution/execute.js:498:12)
    at resolveField (/Users/nwicakson/Documents/TA/informatics-reactql/node_modules/graphql/execution/execute.js:462:16)
    at /Users/nwicakson/Documents/TA/informatics-reactql/node_modules/graphql/execution/execute.js:311:18
    at Array.reduce (<anonymous>)
    at executeFields (/Users/nwicakson/Documents/TA/informatics-reactql/node_modules/graphql/execution/execute.js:308:42)
    at collectAndExecuteSubfields (/Users/nwicakson/Documents/TA/informatics-reactql/node_modules/graphql/execution/execute.js:746:10)
    at completeObjectValue (/Users/nwicakson/Documents/TA/informatics-reactql/node_modules/graphql/execution/execute.js:728:10)
    at completeValue (/Users/nwicakson/Documents/TA/informatics-reactql/node_modules/graphql/execution/execute.js:625:12)
    at /Users/nwicakson/Documents/TA/informatics-reactql/node_modules/graphql/execution/execute.js:582:14
    at <anonymous

Basic GraphQL Question

I have a really basic question about GraphQL:

in the Session Type you define the user response field as follows:

            user: {
                type: UserType,
                resolve(obj) {
                    return obj.session && obj.session.getUser();
                }

Why do we have to return obj.session and obj.session.getUser()? Naively I would think it would be sufficient to just return obj.session.getUser();

If I try my Naive aproach I get this error message:

GraphQLError: Unknown operation named "null".
    at buildExecutionContext (/home/maximilian/code/reactql/bithive/node_modules/graphql/execution/execute.js:202:13)
    at executeImpl (/home/maximilian/code/reactql/bithive/node_modules/graphql/execution/execute.js:121:15)

Can you explain the syntax to me or hint me to the documentation. I searched at the Resolver part of the GraphQL documentation and the Resolver part in the Apollo Documentation and I think it could be somewhere in here:

https://www.apollographql.com/docs/graphql-tools/resolvers.html#Default-resolver

but to be frank I don't understand what's happening here

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.