Authentication service/subgraph optimized for Sequelize ORM
- User Management
- RBAC
- Email authentication
- Social authentication (Google and Facebook)
- SMS OTP
- Multi Client
- i18n
- Dockerize
- Analytics
- CI/CD
- Create a .env file
copy .env.example
file as .env
cp .env.example .env
or pull one from dotenv-vault, if your team has one
npx dotenv-vault@latest pull --dotenvMe=YOUR-TEAM-DOTENV_ME
- Build docker image
yarn docker:build
- Start container
yarn docker:start -d
yarn docker:cli
- Create a root user
npx babel-node src/scripts/createRoot
-
Create client
- You must pass the
client_id
in their request headers.
- You must pass the
npx babel-node src/scripts/createApp
- List existing clients
npx babel-node src/scripts/listApp
- Seed database (optional)
yarn seed
- Create a
.env.test
file
cp .env.example .env.test
- Create Test database:
yarn createdb:test
- Run tests
yarn test
- Build production image
docker compose -t usmansbk/simple-server:release . -f Dockerfile.production
- Push to Docker Hub
docker push usmansbk/auth-service:release
The server makes use of AWS SES to send emails. Setup your SES account and add the following environment variables. Verify your development email and ensure you have this AWS IAM Policy.
MAIL_FROM=sender@example.com
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=us-east-1
Check the email-templates docs on how to design email templates.
Follow the official Twilio documentation to setup your Twilio account and add your TWILIO_ACCOUNT_SID
, TWILIO_AUTH_TOKEN
, and TWILIO_PHONE_NUMBER
to your .env
file.
-
Create a Firebase project if you don't have one.
-
Go to APIs & Auth > Credentials in the Google Developers Console and copy your OAuth 2.0 Client IDs Web Client
GOOGLE_CLIENT_ID
andGOOGLE_SECRET_KEY
. -
Generate an OAuth2 API v2 id token from Google 0Auth 2.0 Playground to test.
- Create a new Facebook app
- Get your
FACEBOOK_APP_ACCESS_TOKEN
andFACEBOOK_APP_ID
env variables - Navigate to Roles ⟶ Test Users to get a test account access tokens
We upload files via REST
endpoints. Why not File Upload mutation?
To set up your S3
for file storage:
- Add your
AWS_S3_BUCKET
toenv
file - Ensure you've set the full s3 permissions
Follow these instructions to get your CLOUDFRONT_API_ENDPOINT
. We use Amazon CloudFront to provide a caching layer to reduce the cost of image process and the latency of subsequent image delivery. The CloudFront domain name provides cached access to the image handler API.
For a more complex filtering, we mimic the sequelize filter query. In order to filter by associations, we assume all associations are aliased (using the as
option). This alias must have corresponding field in your graphql type. Example:
If you define a User has-many
Task relationship like so,
User.hasMany(Task, { as: "tasks" });
you must define a tasks
field in your graphql User
type schema
type User {
tasks(filter: TaskFilter): TaskList!
}
Refer to the sequelize docs for more info on Operators
Our cursor-based pagination must adhere to a List
interface. This is similar to the relay-connection pagination. But unlike relay, we return our items
as a flat list.
# Example
type TaskList implements List {
items: [Task]!
totalCount: Int!
pageInfo: PageInfo!
}
We eager-load requested fields that have a matching association alias
in their corresponding model. Example: If we have a User has-one
Picture relationship:
User.hasOne(Picture, { as: "avatar" });
# This will eager-load the `avatar` association. Both user and avatar will be fetched in a single SQL query
query {
user {
id
name
avatar {
url
}
}
}
-
Eager-loading only works with
Query
.Mutation
isn't supported -
Nested cursor-paginated fields aren't eager-loaded, and hard to maintain in the frontend.
-
Paginated fields should be added to the root
Query
for the reason above.
Segment allows us to collect data with different analytics tools. To setup our analytics, create a Segment account and add your SEGMENT_WRITE_KEY
to the .env
file.
We use "wrapping exceptions" technique to handle client generated errors. This allows us to take full control of the kind of errors we return, and easily translate them before sending to the end-users.
Internal server errors are logged to sentry. Create a Sentry account and add your SENTRY_DSN
to the .env
file.
We use Eslint AirBnB coding guidelines and import alias. All aliases are prefixed with a ~
. To add a new alias, update the jsconfig.json
, .eslintrc.js
, and babel.config.json
files. We also make use of Husky precommit hook to enforce standard.
Model specific logic should be moved to their associated data sources, and resolver errors should be handled using Wrapping Exception technique.