A Go client for the 42 Intranet API, generated from an OpenAPI 3 description with oapi-codegen, plus a thin wrapper handling OAuth2 client-credentials auth and automatic retries.
⚠️ Unofficial spec. The OpenAPI description in this repo is not provided by 42. It has been hand-crafted from the publicapi.intra.42.fr/apidocreference and from observing real responses. Coverage of the full API surface is currently partial, contributions are very welcome, see Contributing.
💡 Not a Go user? The bundled
openapi.yamlis a standard, self-contained OpenAPI 3 document, it isn't tied tooapi-codegenor to Go. You can feed it into any other client generator (e.g. openapi-generator, openapi-python-client, Swagger Codegen, etc.) to produce a client in Python, TypeScript, Java, Rust, or whatever language you need. See Using the spec in other languages.
- Strongly-typed request/response models generated straight from the OpenAPI spec (
ClientWithResponses) - OAuth2 client-credentials flow with automatic, cached, thread-safe token refresh
- Built-in retry transport for rate limiting, transient server errors, and expired tokens
- Spec split into small, per-resource YAML files under
specs/, bundled and linted with Redocly CLI - The bundled
openapi.yamlis plain OpenAPI 3, language-agnostic, so it can drive client generation for Python, TypeScript, or any other language, not just this Go package
go get github.com/42paris/intraoapi42package main
import (
"context"
"fmt"
"log"
intraoapi42 "github.com/42paris/intraoapi42"
)
func main() {
ctx := context.Background()
config := intraoapi42.ProductionConfig.
WithClientCredentials("your-client-id", "your-client-secret").
WithScopes("public")
client, err := intraoapi42.New(config)
if err != nil {
log.Fatal(err)
}
resp, err := client.GetUsersWithResponse(ctx, &intraoapi42.GetUsersParams{})
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.StatusCode())
}The exact generated method and parameter names (
Get...WithResponse,...Params, etc.) come from theoperationIds defined underspecs/paths/and live inopenapi.gen.go. Browse that file (or rungo doc github.com/42paris/intraoapi42) for the full list of currently available endpoints, it will grow as the spec gets more complete.
This repo's real deliverable is arguably the spec itself: openapi.yaml at the repo root is a fully bundled, single-file OpenAPI 3 description with no external $refs left to resolve. It has no dependency on Go or on oapi-codegen, so it works as input to any OpenAPI-compatible generator. For example:
# Python (openapi-python-client)
pip install openapi-python-client
openapi-python-client generate --url https://raw.githubusercontent.com/42paris/intraoapi42/main/openapi.yaml
# Any language, via the generic openapi-generator (needs Java)
npx @openapitools/openapi-generator-cli generate \
-i openapi.yaml \
-g python \
-o ./client-python
# swap "-g python" for "-g typescript-fetch", "-g java", "-g rust", etc.A few things worth knowing if you go this route:
- Always generate from the bundled
openapi.yaml, not from files underspecs/, those are hand-maintained fragments meant to be assembled bymake bundleand aren't valid standalone specs. - Since the spec is hand-crafted and partial (see the note at the top of this README), coverage and accuracy for generators other than
oapi-codegenhaven't been verified as thoroughly, please report issues, they most likely mean the spec needs fixing rather than the generator. - OAuth2 client-credentials handling, retries, and pagination helpers are specific to this Go package, generators for other languages will only give you the typed request/response models and raw HTTP calls, you'll need to write the equivalent auth/retry glue yourself.
New(config) wires up an HTTP client with two layered http.RoundTrippers:
oauth2.Transport, injects theAuthorization: Bearer <token>header, backed by arefreshableTokenSourcethat caches the token in memory (guarded by a mutex) and only hits the token endpoint again once the cached token is invalid or expired.retryTransport, wraps the above and retries the request based on the response status:
| Status | Behavior |
|---|---|
429 Too Many Requests |
Retried up to 3 times, waiting 1s between attempts |
500 Internal Server Error |
Retried up to 5 times, waiting 500ms between attempts |
401 Unauthorized |
The cached token is invalidated (forcing a fresh token fetch on the next attempt) and the request is retried |
.
├── client.go # Config, New(), retry transport, token source
├── time.go # custom time handling for the intra API's date/time formats
├── generate.go # go:generate directive driving oapi-codegen
├── openapi.gen.go # generated Go client (do not edit by hand)
├── openapi.yaml # bundled, single-file OpenAPI spec (generated, do not edit by hand)
├── specs/ # hand-maintained OpenAPI spec, split by concern
│ ├── openapi.yaml # spec root, references the folders below
│ ├── paths/ # one file per resource/endpoint group
│ ├── schemas/ # one file per data model
│ └── parameters/ # shared/reusable parameters (e.g. pagination)
└── tool/
├── generate_indexes.py # regenerates each specs/*/_index.yaml
└── requirements.txt
Each folder under specs/ (paths, schemas, parameters) has an auto-generated _index.yaml that aggregates every top-level key defined in that folder into $ref entries, so the root spec can reference the whole folder without listing every file by hand. Never edit _index.yaml files directly, they're regenerated by tool/generate_indexes.py.
The Makefile drives the whole spec pipeline (Docker is the only local dependency, no need to install Python or Node yourself):
make indexes # regenerate specs/{paths,schemas,parameters}/_index.yaml
make bundle # bundle specs/openapi.yaml + all $refs into ./openapi.yaml
make lint # lint ./openapi.yaml with redocly/cli
make all # runs the three steps above, in orderOnce openapi.yaml is up to date, regenerate the Go client bindings:
go generate ./... # runs generate.go, invoking oapi-codegen against openapi.yaml
go build ./...oapi-codegen is declared as a Go tool dependency in go.mod (tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen), so go generate can invoke it via go tool without a separate global install.
If you're generating a client for a different language instead, you only need make all to produce an up-to-date openapi.yaml, then point your generator of choice at it as shown in Using the spec in other languages.
This client is only as good as the spec behind it, and the spec is currently incomplete, plenty of endpoints, schemas, and edge cases from the real 42 API aren't described yet. Contributions of any size are welcome, especially:
- New or missing
paths(endpoints) andschemas(models) - Corrections to existing schemas: wrong types, missing
required/nullable fields, incomplete enums - Better-documented error responses
- Usage examples and documentation improvements
- Fork the repo and create a branch.
- Add or edit YAML under
specs/paths/,specs/schemas/, orspecs/parameters/, one file per resource, mirroring the existing style. Don't hand-edit_index.yamlor the rootopenapi.yaml. - Run
make allto regenerate the indexes, rebuild the bundledopenapi.yaml, and lint it, fix any lint errors before opening a PR. - Run
go generate ./...and confirmgo build ./.../go vet ./...still pass. - Open a PR describing which endpoint(s)/schema(s) you added or changed, and how you verified them against the real API (a sample response, a link into the apidoc, etc.), since the spec is hand-crafted, this kind of provenance is what keeps it trustworthy.