Skip to content

Commit

Permalink
feat: add a rest example which makes use of graphql-modules
Browse files Browse the repository at this point in the history
  • Loading branch information
darkbasic committed Nov 27, 2023
1 parent fb76ef6 commit 194e053
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 32 deletions.
3 changes: 2 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"@examples/magic-link-server-typescript",
"@examples/react-graphql-typescript",
"@examples/react-rest-typescript",
"@examples/rest-express-typescript"
"@examples/rest-express-typescript",
"@examples/rest-express-typescript-without-modules"
],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
Expand Down
24 changes: 24 additions & 0 deletions examples/rest-express-typescript-without-modules/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# rest-express-typescript

This example demonstrate how to use [accounts-js](https://github.com/accounts-js/accounts).

You must have a mongodb server running before starting the server.

## Setup example

In order to be able to run this example on your machine you first need to do the following steps:

- Clone the repository `git clone git@github.com:accounts-js/accounts.git`
- Install project dependencies: `yarn install`
- Compile the packages `yarn run compile`
- Go to the example folder `cd examples/rest-express-typescript`

## Getting Started

Start the app.

```
yarn run start
```

Open a browser and navigate to [http://localhost:4000](http://localhost:4000).
19 changes: 19 additions & 0 deletions examples/rest-express-typescript-without-modules/now.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": 2,
"name": "rest-express-typescript",
"builds": [
{
"src": "lib/index.js",
"use": "@now/node-server"
}
],
"routes": [
{
"src": ".*",
"dest": "/lib/index.js"
}
],
"env": {
"MONGO_URL": "@accounts-js-rest-express"
}
}
23 changes: 23 additions & 0 deletions examples/rest-express-typescript-without-modules/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "@examples/rest-express-typescript-without-modules",
"private": true,
"version": "0.32.0",
"main": "lib/index.js",
"license": "MIT",
"scripts": {
"start": "NODE_ENV=development yarn run -T nodemon -w src -x ts-node src/index.ts",
"build": "yarn run -T tsc",
"test": "yarn run build"
},
"dependencies": {
"@accounts/mongo": "^0.34.1",
"@accounts/password": "^0.32.2",
"@accounts/rest-express": "^0.33.1",
"@accounts/server": "^0.33.1",
"body-parser": "1.20.2",
"cors": "2.8.5",
"express": "4.18.2",
"mongoose": "8.0.1",
"tslib": "2.6.2"
}
}
92 changes: 92 additions & 0 deletions examples/rest-express-typescript-without-modules/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import 'reflect-metadata';
import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
import mongoose from 'mongoose';
import { AccountsServer, ServerHooks } from '@accounts/server';
import { AccountsPassword } from '@accounts/password';
import accountsExpress, { userLoader } from '@accounts/rest-express';
import { Mongo } from '@accounts/mongo';

mongoose.connect(process.env.MONGO_URL || 'mongodb://localhost:27017/accounts-js-rest-example');
const db = mongoose.connection;

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());

interface UserDoc extends mongoose.Document {
firstName: string;
lastName: string;
}

const User = mongoose.model<UserDoc>(
'User',
new mongoose.Schema({ firstName: String, lastName: String })
);

const accountsPassword = new AccountsPassword({
// This option is called when a new user create an account
// Inside we can apply our logic to validate the user fields
validateNewUser: (user) => {
// For example we can allow only some kind of emails
if (user.email.endsWith('.xyz')) {
throw new Error('Invalid email');
}
return user;
},
});

const accountsServer = new AccountsServer(
{
tokenSecret: 'secret',
},
{
password: accountsPassword,
},
new Mongo(db)
);

accountsServer.on(ServerHooks.ValidateLogin, ({ user }) => {
// This hook is called every time a user try to login.
// You can use it to only allow users with verified email to login.
// If you throw an error here it will be returned to the client.
console.log('Logged in', user);
});

/**
* Load and expose the accounts-js middleware
*/
app.use(accountsExpress(accountsServer));

/**
* Return the current logged in user
*/
app.get('/user', userLoader(accountsServer), (req, res) => {
res.json({ user: (req as any).user });
});

/**
* Expose a public route to edit user informations
* - route is protected
* - update the current logged in user in the db
*/
app.put('/user', userLoader(accountsServer), async (req, res) => {
const userId = (req as any).userId;
if (!userId) {
res.status(401);
res.json({ message: 'Unauthorized' });
return;
}
const user = await User.findById(userId).exec();
user.firstName = req.body.firstName;
user.lastName = req.body.lastName;
await user.save();
res.json(true);
});

app.listen(process.env.PORT || 4000, () => {
console.log('Server listening on port 4000');
});
13 changes: 13 additions & 0 deletions examples/rest-express-typescript-without-modules/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"outDir": "./lib",
"target": "es5",
"lib": ["es2015", "esnext.asynciterable"],
"sourceMap": true,
"importHelpers": true,
"skipLibCheck": true,
"isolatedModules": true,
"esModuleInterop": true
},
"include": ["./src/**/*"]
}
3 changes: 3 additions & 0 deletions examples/rest-express-typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"test": "yarn run build"
},
"dependencies": {
"@accounts/module-core": "^0.34.0",
"@accounts/module-mongo": "^0.34.0",
"@accounts/module-password": "^0.34.0",
"@accounts/mongo": "^0.34.1",
"@accounts/password": "^0.32.2",
"@accounts/rest-express": "^0.33.1",
Expand Down
81 changes: 50 additions & 31 deletions examples/rest-express-typescript/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,54 @@ import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
import mongoose from 'mongoose';
import { AccountsServer, ServerHooks } from '@accounts/server';
import { AccountsServer, AuthenticationServicesToken, ServerHooks } from '@accounts/server';
import { AccountsPassword } from '@accounts/password';
import accountsExpress, { userLoader } from '@accounts/rest-express';
import { Mongo } from '@accounts/mongo';
import { createApplication } from 'graphql-modules';
import { createAccountsCoreModule } from '@accounts/module-core';
import { createAccountsPasswordModule } from '@accounts/module-password';
import { createAccountsMongoModule } from '@accounts/module-mongo';

mongoose.connect(process.env.MONGO_URL || 'mongodb://localhost:27017/accounts-js-rest-example');
const db = mongoose.connection;
const dbConn = mongoose.connection;

const app = express();
const app = createApplication({
modules: [
createAccountsCoreModule({ tokenSecret: 'secret' }),
createAccountsPasswordModule({
// This option is called when a new user create an account
// Inside we can apply our logic to validate the user fields
validateNewUser: (user) => {
if (!user.firstName) {
throw new Error('First name required');
}
if (!user.lastName) {
throw new Error('Last name required');
}

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
// For example we can allow only some kind of emails
if (user.email.endsWith('.xyz')) {
throw new Error('Invalid email');
}
return user;
},
}),
createAccountsMongoModule({ dbConn }),
],
providers: [
{
provide: AuthenticationServicesToken,
useValue: { password: AccountsPassword },
global: true,
},
],
});

const expressApp = express();

expressApp.use(bodyParser.json());
expressApp.use(bodyParser.urlencoded({ extended: true }));
expressApp.use(cors());

interface UserDoc extends mongoose.Document {
firstName: string;
Expand All @@ -27,27 +62,11 @@ const User = mongoose.model<UserDoc>(
new mongoose.Schema({ firstName: String, lastName: String })
);

const accountsPassword = new AccountsPassword({
// This option is called when a new user create an account
// Inside we can apply our logic to validate the user fields
validateNewUser: (user) => {
// For example we can allow only some kind of emails
if (user.email.endsWith('.xyz')) {
throw new Error('Invalid email');
}
return user;
},
const controller = app.createOperationController({
context: {},
});

const accountsServer = new AccountsServer(
{
tokenSecret: 'secret',
},
{
password: accountsPassword,
},
new Mongo(db)
);
const accountsServer = controller.injector.get(AccountsServer);
expressApp.use(accountsExpress(accountsServer));

accountsServer.on(ServerHooks.ValidateLogin, ({ user }) => {
// This hook is called every time a user try to login.
Expand All @@ -59,12 +78,12 @@ accountsServer.on(ServerHooks.ValidateLogin, ({ user }) => {
/**
* Load and expose the accounts-js middleware
*/
app.use(accountsExpress(accountsServer));
expressApp.use(accountsExpress(accountsServer));

/**
* Return the current logged in user
*/
app.get('/user', userLoader(accountsServer), (req, res) => {
expressApp.get('/user', userLoader(accountsServer), (req, res) => {
res.json({ user: (req as any).user });
});

Expand All @@ -73,7 +92,7 @@ app.get('/user', userLoader(accountsServer), (req, res) => {
* - route is protected
* - update the current logged in user in the db
*/
app.put('/user', userLoader(accountsServer), async (req, res) => {
expressApp.put('/user', userLoader(accountsServer), async (req, res) => {
const userId = (req as any).userId;
if (!userId) {
res.status(401);
Expand All @@ -87,6 +106,6 @@ app.put('/user', userLoader(accountsServer), async (req, res) => {
res.json(true);
});

app.listen(process.env.PORT || 4000, () => {
expressApp.listen(process.env.PORT || 4000, () => {
console.log('Server listening on port 4000');
});
19 changes: 19 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4011,10 +4011,29 @@ __metadata:
languageName: unknown
linkType: soft

"@examples/rest-express-typescript-without-modules@workspace:examples/rest-express-typescript-without-modules":
version: 0.0.0-use.local
resolution: "@examples/rest-express-typescript-without-modules@workspace:examples/rest-express-typescript-without-modules"
dependencies:
"@accounts/mongo": "npm:^0.34.1"
"@accounts/password": "npm:^0.32.2"
"@accounts/rest-express": "npm:^0.33.1"
"@accounts/server": "npm:^0.33.1"
body-parser: "npm:1.20.2"
cors: "npm:2.8.5"
express: "npm:4.18.2"
mongoose: "npm:8.0.1"
tslib: "npm:2.6.2"
languageName: unknown
linkType: soft

"@examples/rest-express-typescript@workspace:examples/rest-express-typescript":
version: 0.0.0-use.local
resolution: "@examples/rest-express-typescript@workspace:examples/rest-express-typescript"
dependencies:
"@accounts/module-core": "npm:^0.34.0"
"@accounts/module-mongo": "npm:^0.34.0"
"@accounts/module-password": "npm:^0.34.0"
"@accounts/mongo": "npm:^0.34.1"
"@accounts/password": "npm:^0.32.2"
"@accounts/rest-express": "npm:^0.33.1"
Expand Down

0 comments on commit 194e053

Please sign in to comment.