Local Schema

First define local schema (schema.local.graphql):

"""
Extend type: https://graphql.github.io/graphql-spec/draft/#sec-Object-Extensions
"""
extend type Article {
draft: String!
}
"""
Or add new query: https://github.com/facebook/relay/issues/1656#issuecomment-382461183
"""
extend type Query {
errors: [Error!]
}
type Error {
id: ID!
message: String!
}

Run Relay Compiler as usual (no special option required):

$ relay-compiler --src ./packages --schema ./packages/schema.graphql --verbose

File schema.local.graphql must be somewhere in src folder with the *.graphql extension. You should be good to go - just fetch the fields as usual. You have to commit local update to fill these fields and types:

Relay.commitLocalUpdate(environment, store => {
const articleID = 'f9496862-4fb7-4a09-bc05-a9a3ce2cb7b3'; // ID of the `Article` type you want to update
store.get(articleID).setValue('My custom draft text', 'draft');
// or create new types:
const root = store.getRoot();
const errRecord = store.create('ID_1', 'Error');
errRecord.setValue('ID_1', 'id');
errRecord.setValue('My custom error message', 'message');
root.setLinkedRecords([errRecord, ...], 'errors');
});

More info here: http://facebook.github.io/relay/docs/en/relay-store.html

Protip: create many local GraphQL extensions closely related to one specific part of your application. For example you could create gdsv.local.graphql with the following content:

extend type PNRInfo {
successMessage: String
}

This way I created successMessage client field on PNRInfo type and it should be more or less obvious that it's related only to this GDSV part. All local schemas are being auto-discovered thanks to *.graphql file extension. You can now fetch and render this success message somewhere in GDSV application. Propagation of this message is trivial (you have to fetch the PNRInfo ID):

Relay.commitLocalUpdate(environment, store => {
store
.get(response.id) // unique opaque ID identifying PNRInfo record
.setValue(
'Request has been successfully sent.', // the actual message
'successMessage', // client field name
);
});

Please note that client schema is still somehow experimental feature and that server may introduce the same field successMessage which will conflict with the client one (new kind of BC break). Luckily, Relay will recognize this BC break and it will throw an error:

ERROR:
Field "PNRInfo.successMessage" already exists in the schema. It cannot also be defined in this type extension.

Related resources:

Interesting helper from @sibelius:

export const setLocal = (query: GraphQLTaggedNode, localData: object) => {
const request = getRequest(query);
const operation = createOperationDescriptor(request, {});
env.commitPayload(operation, localData);
env.retain(operation.root);
};

(source)