PubSweet

Server

npm MIT license build status

The pubsweet-server module handles connections to the database layer, contains the core data models, and exposes a REST API and GraphQL API

Database layer

pubsweet-server connects to a PostgreSQL database.

Ways of setting up PostgreSQL

There are a few ways to set up a PostgreSQL instace, ordered here by most recommended, descending:

  1. A docker-compose configuration is provided for easy creation of a PostgreSQL instance, which can be used with docker-compose up db or yarn start:services from an applications folder. In this case the apps are preconfigured and will connect to this database - no additional configuration is needed.
  2. Local installation of the database with something like the Postgres app (Mac), PostgreSQL Windows installers, or similar. You need to provide your own configuration details in pubsweet-server.db (configuration details), for example:

     'pubsweet-server': {
       db: { 
         database: 'yourdatabasename',
         port: 5432,
         username: 'special',
         password: 'special2',
         host: 'localhost'
       }
     }
    
  3. Remote/hosted PostgreSQL instance, for example Amazon's RDS. Same configuration options as above are needed here. This is mostly useful in a production setting, but can be used for development too, if more convenient.

Data models

The data models provided in pubsweet-server provide a foundation for most publishing applications:

User model [code on gitlab]

example usage

const User = require('pubsweet-server/src/models/User')

// create an admin user
const user = new User({
  username: 'admin',
  email: 'admin@website.net',
  password: 'correct-horse-battery-staple',
  admin: true
})

// save to the DB (this populates the object with its ID)
await user.save()

console.log('Saved admin user has ID:', user.id)

Collection model [code on gitlab]

A Collection is a container for Fragments. It is an abstraction to represent any high-level grouping of published units like a book, a journal, an issue, a blog, and so on. Every PubSweet app must have at least one collection.

example usage

const Collection = require('pubsweet-server/src/models/Collection')

const title = 'journalOfKnowledge'
const created = Date.now()

// create a new collection
const collection = new Collection({ title, created })

// set the owner of the collection
// requires that we have a previously saved User instance
collection.setOwners([user.id])

// save to the DB (this populates the object with its ID)
await collection.save()

console.log('Created collection with ID: ', collection.id)

Fragment model [code on gitlab]

A Fragment represents a published unit like a chapter, an article, a blog post, a comment, and so on. Fragments can belong to one or more Collections. It can be owned by one or more Users.

example usage

const Collection = require('pubsweet-server/src/models/Collection')
const journal = await collection.findByField('name', 'journalOfKnowledge')

const Fragment = require('pubsweet-server/src/models/Fragment')

const opts = {
  title: 'A proof that P=NP'
}
const article = new Fragment(opts)

journal.addFragment(article)

article.setOwners([req.user])

console.log(`New team ${article.name} created with ID: ${article.id}`)

Team model [code on gitlab]

example usage

// teams are created around collections or fragments
const Collection = require('pubsweet-server/src/models/Collection')
const journal = await findByField('name', 'journalOfKnowledge')

const Team = require('pubsweet-server/src/models/Team')

const opts = {
  name: 'Journal of Knowledge Editors',
  teamType: {
    name: 'Production Editor',
    permissions: 'all'
  },
  object: journal
}
const editors = await new Team(opts)

console.log(`New team ${editors.name} created with ID: ${editors.id}`)

Authsome [code on gitlab]

Is a module that implements ABAC (attribute-based access control), to determine whether users are authorized to perform requested operations, based on:

example usage

Authsome can be used in isolation, like this:

const Authsome = require('authsome')
const authsome = new Authsome({...config.authsome, mode})

const user = 'user1'
const operation = 'POST'
const resource = '/fragments/12345'

const haspermission = authsome.can(user, operation, resource) // is true or false

However, it is usually used inside an express route function, checking the authorization data provided in the HTTP request to see if the endpoint operation is permitted.

Here's an example of how a route might use authsome:


api.delete('/users/:id', authBearer, async (req, res, next) => {
  try {
    let user = await User.find(req.params.id)
    const permission = await authsome.can(req.user, req.method, user)

    if (!permission) {
      throw authorizationError(req.user, req.method, req.path)
    }
    user = await user.delete()
    return res.status(STATUS.OK).json(user)
  } catch (err) {
    next(err)
  }
})

You can see real examples of Authorize in use wherever pubsweet-server defines an API endpoint, for example the GET /teams endpoint, which calls Authorize in this applyPermissionFilter helper.

REST API

pubsweet-server ships with a REST API that includes the following endpoints:


GraphQL

If configured (if pubsweet-server.enableExpirementalGraphql is set to true) a GraphQL API is available at /graphql. To explore the features of the API you can use GraphiQL, which (if configured i.e. if pubsweet-server.graphiql is set to true) is hosted at /graphiql.


Next let's talk about the client module