Skip to content

Commit 31eb466

Browse files
committed
Add Apollo Server example
1 parent 6b7ccb3 commit 31eb466

File tree

10 files changed

+459
-113
lines changed

10 files changed

+459
-113
lines changed

README.md

Lines changed: 8 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -14,116 +14,13 @@
1414
$ yarn add salesforce-graphql jsforce
1515
```
1616

17-
### Example
17+
## Examples
1818

19-
#### `app.ts`
19+
Refer to the `examples` folder for examples:
2020

21-
```ts
22-
import * as jsforce from 'jsforce';
23-
import * as path from 'path';
24-
import * as fs from 'fs';
21+
> https://github.com/jpmonette/salesforce-graphql/tree/master/examples/apollo.
2522
26-
import { GraphQLServer } from 'graphql-yoga';
27-
import { Binding } from 'salesforce-graphql';
28-
29-
const schemaFile = path.join(__dirname, 'schema.graphql');
30-
const typeDefs = fs.readFileSync(schemaFile, 'utf8');
31-
32-
const { USERNAME, PASSWORD } = process.env;
33-
34-
const resolvers = {
35-
Query: {
36-
Accounts: (parent, args, context, info) =>
37-
context.db.query({}, info).then(res => res.records),
38-
Account: (parent, args, context, info) =>
39-
context.db.query({}, info).then(res => res.records[0]),
40-
Contacts: (parent, args, context, info) =>
41-
context.db.query({}, info).then(res => res.records),
42-
Contact: (parentobj, args, context, info) =>
43-
context.db.query({}, info).then(res => res.records[0]),
44-
},
45-
Account: {
46-
Contacts: (parent, args, context, info) =>
47-
context.db.query({ AccountId: parent.Id }, info).then(res => res.records),
48-
},
49-
Contact: {
50-
Account: (parent, args, context, info) =>
51-
context.db.query({ Id: parent.AccountId }, info).then(res => res.records[0]),
52-
},
53-
};
54-
55-
const conn = new jsforce.Connection({});
56-
57-
function init() {
58-
const db = new Binding({ conn });
59-
60-
const server = new GraphQLServer({
61-
typeDefs,
62-
resolvers,
63-
context: req => ({ ...req, db }),
64-
});
65-
66-
server.start({ playground: '/playground' }, ({ port }) =>
67-
console.log('Server is running on localhost:' + port)
68-
);
69-
}
70-
71-
conn.login(USERNAME, PASSWORD, (err, userinfo) => init());
72-
```
73-
74-
#### `schema.graphql`
75-
76-
```graphql
77-
type Query {
78-
Account(Id: ID!): Account
79-
Accounts(limit: Int): [Account]
80-
Contact(Id: ID!): Contact
81-
Contacts(limit: Int): [Contact]
82-
}
83-
84-
type Account {
85-
Id: ID!
86-
IsDeleted: Boolean
87-
Name: String
88-
Type: String
89-
90-
Contacts(limit: Int): [Contact]
91-
}
92-
93-
type Contact {
94-
Id: ID!
95-
Account: Account
96-
AccountId: String
97-
LastName: String
98-
FirstName: String
99-
Salutation: String
100-
Name: String
101-
}
102-
```
103-
104-
When you are ready, start the GraphQL server:
105-
106-
```sh
107-
$ yarn start
108-
```
109-
110-
Head over to `http://localhost:4000/playground` to test with the following query:
111-
112-
```graphql
113-
{
114-
Account(Id: "001E000001KnMkTIAV") {
115-
Id
116-
Name
117-
Contacts(limit: 1) {
118-
Name
119-
AccountId
120-
Account {
121-
Name
122-
}
123-
}
124-
}
125-
}
126-
```
23+
## Sample GraphIQL Output
12724

12825
![Sample Output](assets/output.png)
12926

@@ -135,10 +32,10 @@ Head over to `http://localhost:4000/playground` to test with the following query
13532

13633
## References
13734

138-
- [`salesforce-graphql` on NPM](https://www.npmjs.com/package/salesforce-graphql)
139-
- Learn more about [GraphQL](http://graphql.org/)
140-
- [Salesforce REST API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_what_is_rest_api.htm) documentation
35+
* [`salesforce-graphql` on NPM](https://www.npmjs.com/package/salesforce-graphql)
36+
* Learn more about [GraphQL](http://graphql.org/)
37+
* [Salesforce REST API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_what_is_rest_api.htm) documentation
14138

14239
## Extra
14340

144-
- Looking for [new opportunities](https://mavens.com/careers/)? Have a look at [Mavens](https://mavens.com/) website!
41+
* Looking for [new opportunities](https://mavens.com/careers/)? Have a look at [Mavens](https://mavens.com/) website!

examples/apollo/package.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "apollo",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"license": "MIT",
6+
"scripts": {
7+
"start": "nodemon -e ts -x ts-node src/index.ts"
8+
},
9+
"dependencies": {
10+
"apollo-server-express": "^1.3.5",
11+
"body-parser": "^1.18.2",
12+
"cookie-parser": "^1.4.3",
13+
"cookie-session": "^2.0.0-beta.3",
14+
"dataloader": "^1.4.0",
15+
"dotenv": "^5.0.1",
16+
"express": "^4.16.3",
17+
"express-session": "^1.15.6",
18+
"graphql": "^0.13.2",
19+
"graphql-tools": "^2.24.0",
20+
"jsforce": "^1.8.4",
21+
"passport": "^0.4.0",
22+
"passport-forcedotcom": "^0.1.4"
23+
},
24+
"devDependencies": {
25+
"@types/node": "^9.6.6"
26+
}
27+
}

examples/apollo/src/index.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { graphiqlExpress, graphqlExpress } from 'apollo-server-express';
2+
import * as bodyParser from 'body-parser';
3+
import * as cookieParser from 'cookie-parser';
4+
import * as cookieSession from 'cookie-session';
5+
import * as DataLoader from 'dataloader';
6+
import * as express from 'express';
7+
import { makeExecutableSchema } from 'graphql-tools';
8+
import * as jsforce from 'jsforce';
9+
import * as passport from 'passport';
10+
import { Strategy } from 'passport-forcedotcom';
11+
12+
import { cookieOptions, strategyOptions } from './lib/config';
13+
import typeDefs from './lib/typeDefs';
14+
import resolvers from './resolvers';
15+
16+
const app = express();
17+
18+
passport.use(new Strategy(strategyOptions, (token, _1, _2, done) => done(null, token.params)));
19+
app.use(cookieParser());
20+
app.use(cookieSession(cookieOptions));
21+
app.use(passport.initialize());
22+
app.use(passport.session());
23+
24+
passport.serializeUser((token, done) => done(null, token));
25+
passport.deserializeUser((token, done) => done(null, token));
26+
27+
app.get('/auth/salesforce', passport.authenticate('forcedotcom'));
28+
29+
app.get(
30+
'/services/oauth2/callback',
31+
passport.authenticate('forcedotcom', { failureRedirect: '/error' }),
32+
(req, res) => res.redirect('/graphiql')
33+
);
34+
35+
app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }));
36+
37+
const executeQuery = ({ access_token, instance_url }, query) =>
38+
new jsforce.Connection({
39+
version: '42.0',
40+
accessToken: access_token,
41+
instanceUrl: instance_url,
42+
})
43+
.query(query)
44+
.then(response => response.records);
45+
46+
const insert = ({ access_token, instance_url }, sobjectName, data) =>
47+
new jsforce.Connection({
48+
version: '42.0',
49+
accessToken: access_token,
50+
instanceUrl: instance_url,
51+
})
52+
.sobject(sobjectName)
53+
.create(data);
54+
55+
app.use('/graphql', bodyParser.json(), (req: any, res, next) => {
56+
const queryLoader = new DataLoader(keys =>
57+
Promise.all(keys.map(query => executeQuery(req.user, query)))
58+
);
59+
60+
const db = { insert: (sobjectName, data) => insert(req.user, sobjectName, data) };
61+
62+
return graphqlExpress({
63+
schema: makeExecutableSchema({ typeDefs: typeDefs, resolvers: resolvers as any }),
64+
context: { user: req.user, loaders: { query: queryLoader }, db },
65+
})(req, res, next);
66+
});
67+
68+
app.listen(3000, () => console.log('Now browse to localhost:3000/graphiql'));

examples/apollo/src/lib/config.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
require('dotenv').config();
2+
3+
export const {
4+
SALESFORCE_USERNAME,
5+
SALESFORCE_PASSWORD,
6+
JWT_SECRET,
7+
CALLBACK_URL,
8+
CLIENT_ID,
9+
CLIENT_SECRET,
10+
} = process.env;
11+
12+
export const cookieOptions = {
13+
name: 'session',
14+
keys: ['key1'],
15+
maxAge: 24 * 60 * 60 * 1000,
16+
};
17+
18+
export const strategyOptions = {
19+
clientID: CLIENT_ID,
20+
clientSecret: CLIENT_SECRET,
21+
scope: ['id', 'chatter_api', 'api'],
22+
callbackURL: CALLBACK_URL,
23+
};

examples/apollo/src/lib/fieldLists.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
export const contactFields = [
2+
'AccountId',
3+
'AssistantName',
4+
'AssistantPhone',
5+
'Birthdate',
6+
'CreatedById',
7+
'CreatedDate',
8+
'CurrencyIsoCode',
9+
'Department',
10+
'Description',
11+
'Email',
12+
'EmailBouncedDate',
13+
'EmailBouncedReason',
14+
'Fax',
15+
'FirstName',
16+
'HomePhone',
17+
'Id',
18+
'IsDeleted',
19+
'IsEmailBounced',
20+
'Jigsaw',
21+
'JigsawContactId',
22+
'LastActivityDate',
23+
'LastCURequestDate',
24+
'LastCUUpdateDate',
25+
'LastModifiedById',
26+
'LastModifiedDate',
27+
'LastName',
28+
'LastReferencedDate',
29+
'LastViewedDate',
30+
'LeadSource',
31+
'MailingAddress',
32+
'MailingGeocodeAccuracy',
33+
'MasterRecordId',
34+
'MobilePhone',
35+
'Name',
36+
'OtherAddress',
37+
'OtherGeocodeAccuracy',
38+
'OtherPhone',
39+
'OwnerId',
40+
'Phone',
41+
'PhotoUrl',
42+
'ReportsToId',
43+
'Salutation',
44+
'SystemModstamp',
45+
'Title',
46+
];
47+
export const accountFields = [
48+
'AccountNumber',
49+
'AccountSource',
50+
'AnnualRevenue',
51+
'BillingAddress',
52+
'BillingGeocodeAccuracy',
53+
'CreatedById',
54+
'CreatedDate',
55+
'CurrencyIsoCode',
56+
'Description',
57+
'Fax',
58+
'Id',
59+
'Industry',
60+
'IsCustomerPortal',
61+
'IsDeleted',
62+
'IsPartner',
63+
'Jigsaw',
64+
'JigsawCompanyId',
65+
'LastActivityDate',
66+
'LastModifiedById',
67+
'LastModifiedDate',
68+
'LastReferencedDate',
69+
'LastViewedDate',
70+
'MasterRecordId',
71+
'Name',
72+
'NumberOfEmployees',
73+
'OwnerId',
74+
'Ownership',
75+
'ParentId',
76+
'Phone',
77+
'PhotoUrl',
78+
'Rating',
79+
'RecordTypeId',
80+
'ShippingAddress',
81+
'ShippingGeocodeAccuracy',
82+
'Sic',
83+
'SicDesc',
84+
'Site',
85+
'SystemModstamp',
86+
'TickerSymbol',
87+
'Type',
88+
'Website',
89+
];

0 commit comments

Comments
 (0)