https://github.com/Illuday/fireQL
FireQL - GraphQL API for Firestore - Boilerplate plug and play
https://github.com/Illuday/fireQL
Last synced: 12 days ago
JSON representation
FireQL - GraphQL API for Firestore - Boilerplate plug and play
- Host: GitHub
- URL: https://github.com/Illuday/fireQL
- Owner: Illuday
- Created: 2019-10-23T19:51:39.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2021-10-15T11:04:13.000Z (over 3 years ago)
- Last Synced: 2024-11-03T15:38:16.220Z (5 months ago)
- Language: JavaScript
- Homepage:
- Size: 330 KB
- Stars: 76
- Watchers: 7
- Forks: 5
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
- awesome-graphql - fireQL - A GraphQL API for Firebase (Firestore database), auto-hosted on cloud functions. (Libraries / JavaScript Libraries)
README
#
Warning
> 🛑🛑 This repository is no longer maintained due to my lack of interest today for this technology. I'm now working on the Napi technology - https://getnapi.com. I added all the sources of the NPM library by the way. Feel free to do whatever you want with it. See you soon! 🛑🛑
> FireQL is a GraphQL connector for Firestore (Firebase database). This repository offer a boilerplate to auto-host a GraphQL server on your Firebase Project (hosting part) connecting to Firestore on the same project.
> **At the moment, use this repository at your own risk, I can't assure the continuity of this project. It's more an experiment for my personnals works than a real technology for en every day use. I'll make it more usable depending of its popularity.**
#
Summary
- **[#](#1) Getting started** – *Create project, environment, clone repository, initialize, run playground*
- **[#](#2) Create a first type**
- **[#](#3) Add documents** – *Adding a mutation to our schema, to our resolvers, execute it*
- **[#](#4) Get documents** – *Adding a query to our schema, to our resolvers, execute it*
- **[#](#5) Update documents** – *Adding a mutation to our schema, to our resolvers, execute it*
- **[#](#6) Remove documents** – *Adding a mutation to our schema, to our resolvers, execute it* - (in development)
- **[#](#7) Working with relations** – *Updating our type, adding inputs, execute fun queries & mutations*
- **[#](#8) Query your API from your application**
- **[#](#9) What's next?**
### Create your Firebase project & your Firestore database
***Note:** Free projects (spark) works with FireQL!*No specifical needs here. Just be sure to create a **Firestore database** and not a realtime one.
### Prepare your environment
Execute these commands in order to install firebase CLI and to login with your Firebase account (Obviously, you need one). Just follow steps.
```sh
$ npm install -g firebase-tools
$ firebase login
```### Clone this repository
Git clone it or just download zip.
```sh
$ git clone https://github.com/Illuday/fireQL.git
```### Init Firebase project
Initialise the firebase project :
```sh
$ firebase init
# Select "Functions" & "Hosting"
# Choose your previously created project
# Use Javascript
# Say "no" to ESlint (You'll be able to install it later.)
# Don't override index.js & packages.json
# Say Yes to dependencies
# Use public directory
# Don't create SPA
# Done!
```
### Open GraphQL playground with function emulatingSetup your Google credentials : https://firebase.google.com/docs/functions/local-emulator#set_up_admin_credentials_optional
```sh
firebase emulators:start
```***Note:** On some systems, emulators doesn't seams to work using this last command. You can downgrade firebase-tools to **6.8.0**, then run :*
```sh
firebase serve
```Access your playground on:
>http://localhost:5001/YOU_PROJECT_ID/us-central1/api (Given in the console)Playground is running! **You have to copy/paste your api link (url above) in the upper field inside the playground in order to make it work**.
### Open GraphQL playground on Firebase Hosting
```sh
firebase deploy
```Then go to Firebase console, section "Functions", you should find your url:
> https://us-central1-YOU_PROJECT_ID.cloudfunctions.net/api
Playground is running! **You have to copy/paste your api link (url above) in the upper field inside the playground in order to make it work**.
### Deploy for production
Todo.
***For non GQL user:** A **type** is a collection / table in your firestore database.*
To create a type, just add it in the schema. You can add as much type you need. **We'll come back on relations later**.
```javascript
type Artist {
id: ID
name: String!
age: Int!
}
```
######functions/graphql/types/artistType.graphql
***For non GQL user:** Compare to a restAPI, a resolver is a GraphQL "route", a mutation will represent a put/patch route with parameters.*
### 1 - Adding the mutation to our schema
```javascript
type Mutation {
addArtist(name: String!, age: Int!): Artist
}
```
######functions/graphql/types/artistType.graphql
### 2 - Adding the mutation to our resolvers
FireQL is my magical library to connect our graphQL server to our firestore. FireQL.add() will automatically add the new artist to our firestore collection "artists".
```javascript
const resolverFunctions = {
Query: {},
Mutation: {
addArtist: (parent, document) => fireQL.add({ collectionName: 'artists', document }),
},
};
```
######functions/graphql/resolvers.js
### 3 - Executing mutation
Go to your GQL playground and execute your mutation. There, we want to add an artist named "illuday", and get his id and his name (probably illuday...).
***For non GQL user:** GraphQL allows you to get only fields you want as result of any queries or mutations.*
####
ADDING AN ARTIST
```javascript
mutation {
addArtist (name: "illuday", age: "28") {
id
name
}
}
```
######Mutation - Playground
:arrow_down:
```json
{
"data": {
"addArtist": {
"id": "BsuNNpRQqFbgWME1RIZ4",
"name": "illuday"
}
}
}
```######
Mutation result - Playground
In firestore, you can see that you have your document, added to artists collection with "illuday" as name and 28 as age.
***Note:** We havn't age in result because we didn't ask for it.*
Magic.
#### We'll come back later on adding, with more powerful add!
### 1 - Adding the query to our schema
```javascript
type Query {
getArtists(where: WhereInput): [Artist]
}
```
######functions/graphql/types/artistType.graphql
**The result value of this query is [Artist], it'll return an Array of Artist type.**
**WhereInput** is an **helper** that provide the **structure for querying firestore**. The object needed here is:
```javascript
{
field: 'nameOfYourField'
operator: 'enum: EQ (==), GT (>), GTE (>=), LE (<), LTE (<=), INARRAY'
value: { // One of
intValue: intValue
stringValue: stringValue
}
}
```***Note:** This helper is already provide in your schema from this repository.*
### 2 - Adding the query to our resolvers
FireQL.get() will automatically get artists from our firestore collection "artists".
```javascript
const resolverFunctions = {
Query: {
getArtists: (parent, { where }) => FireQL.get({ collectionName: 'artists', where }),
},
Mutation: {
...
},
};
```######
functions/graphql/resolvers.js
### 3 - Executing query
Before executing this query, I seed my database to have more artists.
- illuday: 28y/o
- Anna Dittmann: 26y/o
- Ilya Kuvshinov: 29y/o
- Shayline: 27y/oLet's make some tries in our GraphQL playground.
_**Note:** I named my queries (in playground) to be able to save them all._
####
GET ALL ARTISTS
```javascript
query getAllArtists { # <-- This is just a name for GQL playground
getArtists {
id
name
age
}
}
```:arrow_down:
```javascript
{
"data": {
"getArtists": [
{
"id": "GF0ihzKePxeKZMRTjY7A",
"name": "illuday",
"age": 28
},
{
"id": "NVLWsTYEq6GgqvoCvU6W",
"name": "Shayline",
"age": 27
},
{
"id": "TgI9PYG4p7OKzrOBmzmD",
"name": "Anna Dittmann",
"age": 26
},
{
"id": "mPXsd1tkYRfSxN0UW1aQ",
"name": "Ilya Kuvshinov",
"age": 29
}
]
}
}
```
####
GET ARTIST BY ID
```javascript
query getArtistById { # <-- This is just a name for GQL playground
getArtists (where: { field: "id", value: { stringValue: "TgI9PYG4p7OKzrOBmzmD" } }){
id
name
age
}
}
```:arrow_down:
```javascript
{
"data": {
"getArtists": [
{
"id": "TgI9PYG4p7OKzrOBmzmD",
"name": "Anna Dittmann",
"age": 26
}
]
}
}
```
####
GET ARTISTS BY AGE
```javascript
query getArtistsByAge { # <-- This is just a name for GQL playground
getArtists (where: { field: "age", operator: LT, value: { intValue: 28 } }){
name
age
}
}
```:arrow_down:
```javascript
{
"data": {
"getArtists": [
{
"name": "Anna Dittmann",
"age": 26
},
{
"name": "Shayline",
"age": 27
}
]
}
}
```
### 1 - Adding the mutation to our schema
```javascript
type Mutation {
...,
updateArtist(id: ID!, name: String, age: Int): Artist
}
```######
functions/graphql/types/artistType.graphql
### 2 - Adding the mutation to our resolvers
FireQL.update() will automatically update the artist in our firestore collection "artists".
```javascript
const resolverFunctions = {
Query: {
...
},
Mutation: {
...,
updateArtist: (parent, document) => FireQL.update({ collectionName: 'artists', document }),
},
};
```######
functions/graphql/resolvers.js
### 3 - Executing mutation
Let's say we want to modify "illuday" age.
####
UPDATE ILLUDAY AGE
```javascript
mutation updateIlludayAge { # <-- This is just a name for GQL playground
updateArtist(id: "GF0ihzKePxeKZMRTjY7A", age: 38) {
id
name
age
}
}
```:arrow_down:
```javascript
{
"data": {
"updateArtist": {
"id": "GF0ihzKePxeKZMRTjY7A",
"name": "illuday",
"age": 38
}
}
}
```####
## Removing document - (in development)
### 1 - Adding the mutation to our schema
```javascript
type Mutation {
...,
removeArtist(id: ID!): Artist
}
```######
functions/graphql/types/artistType.graphql
### 2 - Adding the mutation to our resolvers
FireQL.remove() will automatically remove the artist in our firestore collection "artists".
```javascript
const resolverFunctions = {
Query: {
...
},
Mutation: {
...,
removeArtist: (parent, document) => FireQL.remove({ collectionName: 'artists', document }),
},
};
```######
functions/graphql/resolvers.js
### 3 - Executing mutation
####
REMOVE ILLUDAY
```javascript
mutation removeIlluday { # <-- This is just a name for GQL playground
removeArtist(id: "GF0ihzKePxeKZMRTjY7A") {
id
}
}
```:arrow_down:
```javascript
{
"data": {
"removeArtist": {
"id": "GF0ihzKePxeKZMRTjY7A"
}
}
}
```####
***Note:** in FireQL, all relations must be bi-directionnal.*
### 1 - Adding a new type and create a relation
Artists have **MANY** artworks, artworks have **ONE** artist.
```javascript
type Artwork {
id: ID
name: String
artist: Artist
}type Artist {
id: ID
name: String!
age: Int!
artworks: [Artwork]
}
```######
functions/graphql/types/artistType.graphql & types/artworkType.graphql
**Follow steps above to create basics queries & mutations for the new type**
### 2 - Modifying Artwork mutation to handle relation management
We need to create our **inputs** before modifying our addArtist & updateArtist mutations. They'll allows those things:
- Add **artworks** when we add an **artist**
- Update / Remove **artworks** when we update **artist**```javascript
input ArtworkInput {
name: String
}input AddArtworkInput {
collection: String = "artworks"
on: String = "artist"connect: ID
create: ArtworkInput
}input UpdateArtworkInput {
collection: String = "artworks"
on: String = "artist"connect: ID
remove: ID
create: ArtworkInput
}
```######
functions/graphql/types/artworkType.graphql
Back to these inputs:
- **ArtworkInput**: Represent fields we can fill when we create an artwork
- **AddArtworkInput**:
| Argument | Value | Description |
| ---------- | ------------------- | ------------------------------------------------------------ |
| collection | String = "artworks" | Name of the collection linked, set **artworks by default**. You'll never have to change that. |
| on | String = "artist" | Foreign field for our relation, set **artwork by default**. You'll never have to change that. ***Note**: In case of a One to Many or Many to Many relations you'll have to write [String] = ['artists'].* |
| connect* | ID | The id of artwork you want to connect with. |
| create* | ArtworkInput | The input of artwork you want to create then link. |*one of these must be fill when you execute the mutation
- **UpdateArtworkInput**:
| Argument | Value | Description |
| ---------- | ------------------- | ------------------------------------------------------------ |
| collection | String = "artworks" | Name of the collection linked, set **artworks by default**. You'll never have to change that. |
| on | String = "artist" | Foreign field for our relation, set **artwork by default**. You'll never have to change that. ***Note**: In case of a One to Many or Many to Many relations you'll have to write [String] = ['artists'].* |
| connect* | ID | The id of an artwork you want to connect with. |
| remove* | ID | The id of an artwork you want to remove. ***Note**: In case of a Many to One or One to one relations, the artwork will be removed from the database* |
| create* | ArtworkInput | The input of artwork you want to create then link. |*one of these must be fill when you execute the mutation
Now that we have inputs, we can adjust our mutations "addArtist" and "updateArtist".
```javascript
type Mutation {
addArtist(name: String!, age: Int!, artworks: [AddArtworkInput]): Artist
updateArtist(id: ID!, name: String, age: Int, artworks: [UpdateArtworkInput]): Artist
...
}
```######
functions/graphql/types/artistType.graphql
That's it! Let's play with it.
### 3 - Executing mutations
####
ADD AN ARTIST AND CREATE ARTWORKS AT THE SAME TIME
```javascript
mutation addAnArtistWithArtworks {
addArtist(
name: "illuday",
age: 28,
artworks: [
{ create: { name: "MIRAMARKA" } }, # NEW ARTWORK
{ create: { name: "BLACKLIST" } }, # NEW ARTWORK
{ connect: "WDwGd4LwjZGfsFEALOi7" } # EXISTING ARTWORK
]
) {
id
name
artworks { id name } # WAIT WHAT ?
}
}
```:arrow_down:
```javascript
{
"data": {
"addArtist": {
"id": "xyseptoQ7WBBRr8XAl4U",
"name": "illuday",
"artworks": [
{
"id": "NmgdsLrNgzIiIUaRFkef",
"name": "MIRAMARKA"
},
{
"id": "W7rgj1Lkc4HAruuMMmX2",
"name": "BLACKLIST"
},
{
"id": "WDwGd4LwjZGfsFEALOi7",
"name": "NYNDOR"
}
]
}
}
}
```**So, what happens there ?** We inserted an new artist in our database, named illuday, we decided to create at the same time two new artworks and connect an already existing one. References between the artist and artworks are automatically set by FireQL.
**And the result ?** You can see that as we added relations in our **artist** types, we can query directly its **artworks** on results (queries, mutations). Yeah!
####
UPDATE AN ARTIST AND REMOVE ONE ARTWORK
***Note:** Due to a Firebase limitation (arrayUnion / arrayRemove), you can't add and remove at the same time.*
```javascript
mutation updateAnArtistWithArtworks {
updateArtist(
id: "xyseptoQ7WBBRr8XAl4U", # illuday
artworks: [
{ remove: "W7rgj1Lkc4HAruuMMmX2" }, # BLACKLIST
]
) {
id
name
artworks { id name }
}
}
```:arrow_down:
```javascript
{
"data": {
"updateArtist": {
"id": "xyseptoQ7WBBRr8XAl4U",
"name": "illuday",
"artworks": [
{
"id": "NmgdsLrNgzIiIUaRFkef",
"name": "MIRAMARKA"
},
{
"id": "WDwGd4LwjZGfsFEALOi7",
"name": "NYNDOR"
}
]
}
}
}
```####
QUERYING OUR FINAL ARTIST
```javascript
query getIlluday {
getArtists(where: {field: "id", value: {stringValue: "xyseptoQ7WBBRr8XAl4U"}}) {
id
name
age
artworks {
id
name
}
}
}
```:arrow_down:
```javascript
{
"data": {
"getArtists": [
{
"id": "xyseptoQ7WBBRr8XAl4U",
"name": "illuday",
"age": 28,
"artworks": [
{
"id": "NmgdsLrNgzIiIUaRFkef",
"name": "MIRAMARKA"
},
{
"id": "WDwGd4LwjZGfsFEALOi7",
"name": "NYNDOR"
},
{
"id": "nTTtND8HyktG30IQzO5M",
"name": "ACTIVITOUR"
}
]
}
]
}
}
```
## Query your API from your application
You just have to use a GQL client (it's like an axios for restAPI), here are some :
- Flutter: https://github.com/zino-app/graphql-flutter
- VueJS: https://github.com/vuejs/vue-apollo
- Nuxt: https://github.com/nuxt-community/apollo-module
- ReactJS: https://github.com/apollographql/react-apollo
- React Native: https://github.com/apollographql/apollo-clientOr for React, Angular, Vue, Ember, Web Components, Meteor, Blaze, Vanilla JS, Next.js and I assume every javascript based framework: https://github.com/apollographql/apollo-client
- [x] Connection to Firestore
- [x] Hosting on Firebase cloud functions
- [x] Playgrounds local & online
- [x] Get documents
- [x] Get documents with their relations
- [x] Add documents
- [x] Add documents and relation (create, connect)
- [x] Update documents
- [x] Update documents and their relations (create, connect, remove)
- [ ] Remove documents
- [ ] Remove documents and their relations
- [ ] Handle authentication (Anonymous, phone, e-mail, Facebook, Google)
- [ ] Simple security rules for queries & mutations
- [ ] Subscriptions (for realtime data)
- [ ] File upload to Firebase storage#### You can choose the feature you need the most:
[](https://api.gh-polls.com/poll/01DR6Y6WSZEMT0SCH1DT0TEM2K/Authentication%20%26%20Security/vote)
[](https://api.gh-polls.com/poll/01DR6Y6WSZEMT0SCH1DT0TEM2K/Subscriptions/vote)
[](https://api.gh-polls.com/poll/01DR6Y6WSZEMT0SCH1DT0TEM2K/File%20upload/vote)