Opa Diego, tudo bem ?
Então cara, quando você usa o save do hibernate, ele começa a gerenciar sua entidade e atribui o id nela, se você imprir o id embaixo daquela linha, você ira perceber o ponto exato onde foi atribuido. Como se dentro do save ele chamasse o topico.setId(id).
@PostMapping("topico")
public ResponseEntity<TopicoDto> cadastrar(@RequestBody TopicoForm form, UriComponentsBuilder uriBuilder) {
Topico topico = form.convertParaTopico(cursoRepository);
System.out.println(topico.getId()); //NULL
topicoRepository.save(topico);
System.out.println(topico.getId()); //O ID FOI PREENCHIDO
//Abaixo teriamos o retorno do método save
//topico = topicoRepository.save(topico);
URI uri = uriBuilder.path("/api/topico/{id}").buildAndExpand(topico.getId()).toUri();
return ResponseEntity.created(uri).body(new TopicoDto(topico));
}
Espero que tenha ficado claro.